Init
This commit is contained in:
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC 2020_『ブループロトコル』小さな“アニメらしさ”が積もり積もって、劇場アニメに入り込んだような体験を生む.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC 2020_『ブループロトコル』小さな“アニメらしさ”が積もり積もって、劇場アニメに入り込んだような体験を生む.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2019_BLUE PROTOCOLの個性豊かなキャラクターを動かす意思決定システム.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2019_BLUE PROTOCOLの個性豊かなキャラクターを動かす意思決定システム.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2020_BLUE PROTOCOLのパーティバトルを支える集団制御AI.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2020_BLUE PROTOCOLのパーティバトルを支える集団制御AI.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2021_BLUE PROTOCOLにおけるキャラクタークリエーション.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2021_BLUE PROTOCOLにおけるキャラクタークリエーション.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2021_BLUEPROTOCOLにおけるアニメ表現手法(実装編).pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2021_BLUEPROTOCOLにおけるアニメ表現手法(実装編).pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2021_BLUEPROTOCOLにおけるビルドパイプラインのクラウド化の壁.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2021/CEDEC2021_BLUEPROTOCOLにおけるビルドパイプラインのクラウド化の壁.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2023/CEDEC2023_applibot0909.ppsx
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2023/CEDEC2023_applibot0909.ppsx
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2023/『バンドリ! ガールズバンドパーティ!』リアルタイム3Dライブを支えるグラフィックとパイプラインの技術.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2023/『バンドリ! ガールズバンドパーティ!』リアルタイム3Dライブを支えるグラフィックとパイプラインの技術.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2023/自動生成マップの描画を高速化!ComputeShaderを使ったオクルージョンカリングの理論と実装.pptx
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2023/自動生成マップの描画を高速化!ComputeShaderを使ったオクルージョンカリングの理論と実装.pptx
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/CEDEC2024_StrokeGen_submit.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/CEDEC2024_StrokeGen_submit.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/CEDEC2024_gkmas_pipeline.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/CEDEC2024_gkmas_pipeline.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/CEDEC2024_matsuike_jpn.pptx
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/CEDEC2024_matsuike_jpn.pptx
(Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p34.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p34.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p35.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p35.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p65.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p65.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p66.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p66.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p8.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/p8.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌/亀GRANBLUE FANTASY Relink亁僜僼僩僂僃傾儔僗僞儔僀僓偵傛傞幚慔揑側僆僋儖乕僕儑儞僇儕儞僌.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/30儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/30儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/44儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/44儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/49儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/49儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/54儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/54儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/58儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/58儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/60儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/60儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/67儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/67儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/73儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/亀GRANBLUE FANTASY Relink亁嵟崅偺乽杤擖姶乿傪幚尰偡傞僇僢僩僔乕儞惂嶌庤朄偲偦傟傪巟偊傞媄弍/73儁乕僕摦夋.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/神は細部に宿る!「学園アイドルマスター」のこだわり抜いた3Dキャラクター・背景制作.pptx
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/神は細部に宿る!「学園アイドルマスター」のこだわり抜いた3Dキャラクター・背景制作.pptx
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/統合スタイライズドレンダリングシステムの開発について紹介 〜共通利用ができる基盤を目指して〜.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/統合スタイライズドレンダリングシステムの開発について紹介 〜共通利用ができる基盤を目指して〜.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/3Dでキレイな線を引くために。ギルティギアシリーズのトゥーンライン制御テクニック/CEDEC2024ギルティギアトゥーンライン制御テクニック.pptx
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/3Dでキレイな線を引くために。ギルティギアシリーズのトゥーンライン制御テクニック/CEDEC2024ギルティギアトゥーンライン制御テクニック.pptx
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/3Dでキレイな線を引くために。ギルティギアシリーズのトゥーンライン制御テクニック/講演CEDEC2024ギルティギアライン制御テクニック.pptx.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/2024/3Dでキレイな線を引くために。ギルティギアシリーズのトゥーンライン制御テクニック/講演CEDEC2024ギルティギアライン制御テクニック.pptx.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/Early/偶像大师+2+-+《全面升级偶像们的表演》.pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/Early/偶像大师+2+-+《全面升级偶像们的表演》.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/Early/第+3+期++[CEDEC+2010][次世代的偶像大师].pdf
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/CEDEC/Early/第+3+期++[CEDEC+2010][次世代的偶像大师].pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
370
03-UnrealEngine/卡通渲染相关资料/卡通渲染开发总览.md
Normal file
370
03-UnrealEngine/卡通渲染相关资料/卡通渲染开发总览.md
Normal file
@@ -0,0 +1,370 @@
|
||||
---
|
||||
title: 卡通渲染开发总览
|
||||
date: 2023-12-08 09:59:57
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐⭐⭐
|
||||
---
|
||||
# 目录
|
||||
- [[卡通渲染相关资料]]里面的有原神、碧蓝幻想、罪恶装备、赛马娘、黑丝指的参考。
|
||||
- 做完基础功能之后,再逐个寻找新的功能点与方向。
|
||||
- [[知乎FlashYiYi的卡通渲染分享]]里面有许多Fake技巧
|
||||
- 参考对象分析
|
||||
- 星穹铁道
|
||||
- 原神
|
||||
- https://zhuanlan.zhihu.com/p/316138540
|
||||
- 软阴影边界
|
||||
- 反射探针,获取BaseColor、Normal等,最后Relight来获得复合场景的反射CubeMap。
|
||||
- UnityChan
|
||||
- 破晓传说
|
||||
- 蓝色协议
|
||||
- 蓝色反射
|
||||
- 非人学园2
|
||||
- 少女前线2 追放
|
||||
- https://www.bilibili.com/video/BV1pt421H7EH/?buvid=38ddfe416af6e23f1aada0674f307e3e&from_spmid=tm.recommend.0.0&is_story_h5=false&mid=a0AUwJxrG%2FcYg0AorRjOGA%3D%3D&p=1&plat_id=116&share_from=ugc&share_medium=iphone&share_plat=ios&share_session_id=CD0CF499-8DF5-48F5-A03A-7A3AE61FFF3A&share_source=COPY&share_tag=s_i&spmid=united.player-video-detail.0.0×tamp=1706754945&unique_k=44q6hra&up_id=140517136&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
- 明日方舟终末地
|
||||
- [[CEDEC]]
|
||||
- 学园偶像大师
|
||||
- https://zhuanlan.zhihu.com/p/716241556
|
||||
- [CEDEC2024-细微之处见神明《学院偶像大师》中精致的3D角色与背景制作(一)](https://zhuanlan.zhihu.com/p/20174115953)
|
||||
- BanG梦想! 女子乐队派对
|
||||
- 异度神剑
|
||||
- 碧蓝幻想
|
||||
- https://zhuanlan.zhihu.com/p/14858282081
|
||||
- https://zhuanlan.zhihu.com/p/27407759719
|
||||
- 【Cygames技术大会 2024】碧蓝幻想Relink - 角色模型制作事例 - Ale Feng的文章 - 知乎 https://zhuanlan.zhihu.com/p/16218213290
|
||||
- 归龙潮
|
||||
- 其他人的作品
|
||||
- [蛋白胨](https://www.zhihu.com/people/danbaidong1111) https://zhuanlan.zhihu.com/p/663968812
|
||||
- https://miusjun13qu.feishu.cn/docx/EXPtdrNmnox8hkx4mnCcy8QNn2b
|
||||
- https://github.com/danbaidong1111/DanbaidongRP
|
||||
- https://www.bilibili.com/video/BV147pveZEck/?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
- [muro_cg](https://www.youtube.com/@muro_cg)
|
||||
- [Nevrwind](https://space.bilibili.com/397442)
|
||||
- 【【UE5动画】你会来的对吗?】 https://www.bilibili.com/video/BV1kw4m1e7qZ/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- 【【UE5卡渲】椿 - K表情练习】 https://www.bilibili.com/video/BV1ay411b7Cm/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- 卡通渲染晕染效果【【UE5】一些最近做的好玩的卡渲后效】 https://www.bilibili.com/video/BV1gi421v7HA/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- [【Unity/虚幻5/Blender】3种引擎 绝区零风格 卡通渲染 星见雅 完整流程](https://www.bilibili.com/video/BV1kBBKYRE6Q/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e)
|
||||
- 动漫制作
|
||||
- 2024年4月新番 Girls Band cry【日本最顶级的3D动画制作过程【GBC官方动捕制作片段】】 https://www.bilibili.com/video/BV1uM4m1U7CL/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
|
||||
>PS. 先实现一波Cel,之后再实现Thickly - Coated。
|
||||
|
||||
# 最近开发计划
|
||||
1. [x] 添加ShaderModel
|
||||
1. https://zhuanlan.zhihu.com/p/658700282
|
||||
2. https://zhuanlan.zhihu.com/p/597568404
|
||||
2. [x] 给材质添加一些属性
|
||||
1. [x] 移植ToonDataAsset。
|
||||
1. [x] MaterialInstance Override 完成
|
||||
2. [x] 添加BackFace Outline基础代码。
|
||||
3. [x] 添加材质自定义Output节点。
|
||||
4. [x] 给GBuffer结构体添加子ToonBuffer结构体。
|
||||
5. [x] ShaderModel:ToonStandard
|
||||
1. [x] 实现初版ToonStandard。
|
||||
6. [ ] ToonOutline(后处理)
|
||||
1. [x] ID、Depth、Normal Outline
|
||||
1. [x] 深度描边初版
|
||||
2. [x] 法线描边初版
|
||||
3. [x] FOV宽度适配
|
||||
4. [x] ScreenPercentage、DPI宽度适配(包括Editor Overrider)
|
||||
5. [x] 后处理描边的内外描边问题。深度描边会描内部与外部。导致一些比较近的地方深度描边比较细。
|
||||
1. [x] 内外描边通过是否是卡通材质来判断。所以分为 外内与内内2种情况。
|
||||
2. [x] 外描边之后考虑使用BackFace来实现。
|
||||
6. [x] ToonOutlineDataAsset 添加深度&法线描边开关选项。
|
||||
7. [x] ToonOutlineDataAsset ID描边实现。
|
||||
8. [x] ToonOutlineID Overrider实现。可以使用OutlineID贴图来实现单材质描边的功能。
|
||||
9. [x] Fix SkyAtmosphere 与ExponentialHeightFog会干掉Outline的问题。**目前将ToonOutline移到SkyAtmosphere、ExponentialHeightFog Pass之后** 另一个思路就是使用BackfaceOutline
|
||||
10. [x] Outine PostProcess Overrider设置。 ToonOutline.cpp 42行。
|
||||
11. [ ] TODO: TSR Outline写入速度信息。
|
||||
1. [ ] ue4.26 体积云重投影 https://zhuanlan.zhihu.com/p/718922764
|
||||
12. [ ] Nanite支持[[Nanite学习笔记]]
|
||||
2. [ ] OutlineMask& SDF Outline
|
||||
1. [x] 8通道存储SDFOutlineMask**8位精度不够,在Materal中计算依然有锯齿**
|
||||
1. [x] https://zhuanlan.zhihu.com/p/410710318
|
||||
2. [x] https://zhuanlan.zhihu.com/p/113190695
|
||||
2. [ ] 剩下8通道
|
||||
1. [ ] 3通道分别控制ID、Depth、Normal Outline Mask。
|
||||
2. [ ] Offset、Feather?
|
||||
3. PS绘画出不同压感的颜色。
|
||||
1. F5打开画笔设置对话框,点击“传递”页。
|
||||
2. 将“不透明度抖动”设为0,并将控制方式设置为钢笔压力。最小钢笔压力设置成50%。
|
||||
3. 如果效果不明显,将“流量抖动”设置组也一样设置。
|
||||
4. [ ] 编写Outline贴图转换工具。
|
||||
1. [ ] https://zhuanlan.zhihu.com/p/658213216 https://github.com/danbaidong1111/UnityTools/tree/main/SDFGenerateTool
|
||||
2. [ ] https://zhuanlan.zhihu.com/p/702637242
|
||||
3. [ ] BackFaceOutline
|
||||
1. [x] https://zhuanlan.zhihu.com/p/613772622
|
||||
2. [x] https://zhuanlan.zhihu.com/p/552283835
|
||||
3. [x] VertexShader,模型反向压平。
|
||||
4. [x] fix 模型勾选渲染CustomDepth才能正常渲染的bug
|
||||
5. [x] fix 开启一个模型的CustomDepth会让其他ToonStandard模型开启CustomDepth写入。(将CustomDepth写入 => Depth写入)
|
||||
6. [x] 深度剔除bug解决
|
||||
7. [x] 使用比较ToonMeshDrawOutline前后深度的方法来获取ToonMeshDrawOutlineMask.
|
||||
8. [ ] TODO:确认VR中的运行结果是否正确?
|
||||
9. [ ] TODO:Nanite模型支持。
|
||||
10. [ ] TODO:另一种思路定点内扩+Stencil的方式渲染内描边?
|
||||
11. [ ] TODO:顶点法线工具开发 https://tajourney.games/7689/
|
||||
4. [ ] TODO:RayTacing描边方法。
|
||||
7. [ ] ToonLighting
|
||||
1. [ ] Cel
|
||||
2. [ ] 【UE5】卡通渲染着色篇3:多光源 https://zhuanlan.zhihu.com/p/717533663
|
||||
3. [ ] UE5 浅谈光源更新多线程优化 https://zhuanlan.zhihu.com/p/27574076122?utm_psn=1879905019928764942
|
||||
8. [ ] ToonPostProcess
|
||||
1. [x] ToonBloom
|
||||
2. [ ] ToonDiffusion:对场景亮部进行降采样,之后将亮度信息叠加到Toon物体的暗部上。
|
||||
3. [ ] 晕染效果,Background透过光功能。[[幻塔-Background透过光.png]]
|
||||
4. [ ] 实现一波Anti-Lut
|
||||
5. [x] 考虑其他风格的ToneMapper(可能还是使用LUT更好调整) https://zhuanlan.zhihu.com/p/716759929?utm_psn=1812047639874760707
|
||||
1. [x] CCA Tonemapping
|
||||
2. [x] GT Tonemapping https://github.com/yaoling1997/GT-ToneMapping
|
||||
3. [x] AGX Tonemapping https://github.com/EaryChow/AgX
|
||||
4. [ ] 其他Tonemapping: Local Tonemapping方案总结 https://zhuanlan.zhihu.com/p/519457212
|
||||
5. [ ] ***Fix 运行的时候出现的渲染错误***。
|
||||
9. [ ] ToonRimLighting
|
||||
1. [x] 材质边缘光(位于引擎插件/ToonUtility/MaterialFunction/MF_RimLighting)
|
||||
2. [ ] 后处理边缘光
|
||||
3. [ ] Matcap
|
||||
4. [ ] ASoul边缘光
|
||||
10. [ ] 关于逆ToneMapping映射的万能人肉求解法 https://zhuanlan.zhihu.com/p/14603997646
|
||||
11. [ ] Reflection控制
|
||||
1. [x] ReflectionAlpha实现。
|
||||
2. [ ] Matcap Reflection实现。
|
||||
3. [ ] 对Reflection Texture进行Kuawahara处理,之后根据亮度抽象成第二高光(提取亮度之后,乘以高光颜色)
|
||||
1. 【[UFSH2024]用虚幻引擎5为《幻塔》定制高品质动画流程风格化渲染管线 | 晨风 Neverwind 完美世界游戏】 【精准空降到 14:24】 https://www.bilibili.com/video/BV1rW2LYvEox/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=864
|
||||
12. [ ] Toon GI(Lumen / Other)
|
||||
1. [ ] DiffuseIndirect Alpha / DiffuseIndirect Saturation / AOAlpha
|
||||
1. [x] Lumen
|
||||
2. [ ] Other GI Method
|
||||
2. [ ] 向下采样
|
||||
3. [ ] 平滑法线 => 平滑饱和度
|
||||
1. [ ] 使用哪个https://zhuanlan.zhihu.com/p/25839790454
|
||||
2. [ ] 尝试使用高斯模糊Lumen法线。
|
||||
3. [ ] 尝试使用ViewVector来平滑法线。
|
||||
1. 用相机方向的话,随着相机绕角色旋转,角色身上亮度变化会比较明显,所以视频里用的是把平滑度乘在Lumen的1-2阶球谐上
|
||||
4. [ ] 尝试平滑二阶、三阶球谐,来使得Lumen效果低频化
|
||||
1. 伪代码 OutSH = SH0 + (SH1 + SH2) * Smoothness;
|
||||
5. [ ] Cel适配,需要实现[[Toon多光源参考|Toon多光源后处理Pass]]
|
||||
13. [ ] 阴影控制
|
||||
1. [x] 控制深度偏移
|
||||
1. [x] 在材质中使用ShadowPassSwitch再对ViewSpace的Z轴方向(使用DirectionalLightVector比较可以只对方向光进行偏移)进行WPO偏移实现。
|
||||
2. [ ] ContactShadow接触阴影实现衣服细节的DetailShadow
|
||||
3. [x] 半程阴影(DirectionOffsetToViewShadow)半分阴影[[幻塔-半程自阴影.png]]
|
||||
4. [x] 给图元添加禁用自阴影功能——发现启用RecivedViewOffsetShadow就可以起到这个功能。
|
||||
14. [ ] ToonHair
|
||||
1. [【UE5】通过深度偏移计算刘海投影](https://zhuanlan.zhihu.com/p/690066418)
|
||||
2. [UE5 NPR-Shadow-Character Shadow(2)](https://zhuanlan.zhihu.com/p/13953884160)
|
||||
15. [ ] ToonTranslucent
|
||||
1. [ ] 通过模仿SingleLayerWater实现折射效果 https://zhuanlan.zhihu.com/p/657928532
|
||||
2. [ ] 眉眼细节 [[幻塔-眉眼细节.png]]
|
||||
1. [ ] 【【Unity/虚幻5/Blender】3种引擎 崩坏: 星穹铁道风格 卡通渲染 从球谐光照到眉毛透过刘海 完整流程】 【精准空降到 1:24:29】 https://www.bilibili.com/video/BV1CN411C7qx/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=5069
|
||||
3. [ ] [【UE5】逐材质stencil、半透明排序、OverlayMaterial](https://zhuanlan.zhihu.com/p/31171114411)
|
||||
16. [ ] TSR
|
||||
1. [ ] [UE TAAU详细解析](https://zhuanlan.zhihu.com/p/681910495)
|
||||
2. [ ] [UE TSR详细解析](https://zhuanlan.zhihu.com/p/682350835)
|
||||
3. [ ] [GDC2025 : Generalized Stylized Post Processing Outline](https://zhuanlan.zhihu.com/p/1895785690161198348)
|
||||
17. [ ] [[Toon多光源参考|Toon多光源后处理Pass]]
|
||||
18. [ ] [【虚幻5】UE_动画变形工具(晶格变形)](https://www.bilibili.com/video/BV1DsR3YoEML/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e)
|
||||
19. [ ]
|
||||
20. [ ] 掰法线相关
|
||||
1. [ ] 【Blender插件 Easy Custom Normals V1.1.0汉化版,卡通渲染优化利器!-哔哩哔哩】 https://b23.tv/meLiEkj
|
||||
1. [ ] https://superhivemarket.com/products/stylized-normals
|
||||
2. [ ] https://tools.fondant.gg/
|
||||
2. [ ] 月下幻影的掰法线思路:`N = _Scale * NoL * L + N`尝试找到使用场景。资产路径`/Game/ToonContentExample/MaterialUtility/NormalOffsetToDirectionalLight/M_NormalOffsetToDirectionalLight`
|
||||
21. [ ] RGS 光线追踪与路径追踪支持。
|
||||
1. [ ] [【UE5】通过Raytracing自定义卡通渲染投影](https://zhuanlan.zhihu.com/p/688722298) https://zhuanlan.zhihu.com/p/688722298
|
||||
22. [ ] 给EditorView添加卡通渲染用场景,默认场景的渲染效果对不上。
|
||||
23. [ ] 实现前向混合管线?思路有2:1. Material直接计算光照结果。2. 自定义ToonBasePass到LightingPass后,复制阴影贴图与Lumen渲染结果进行Composition。
|
||||
1. 原神早期的方案:
|
||||
1. 渲染地形(远景
|
||||
2.
|
||||
3. CubeMap环境光照,(和时间与地区有关)
|
||||
4. 渲染屏幕空间深度图
|
||||
5. 渲染角色以及近景模型(卡通材质相关渲染Pass)
|
||||
1. 有计算直接光照与阴影。使用屏幕空间深度图来计算阴影。
|
||||
6. 渲染深度与法线Buffer。
|
||||
7. 渲染级联阴影。
|
||||
8. 合成,阴影与环境光照(AO、环境探针)。
|
||||
24. [ ] 云彩生成器
|
||||
1. [ ] https://www.bilibili.com/video/BV1L5kdYFEXc/?spm_id_from=333.1007.tianma.4-1-11.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
2. [ ] 风格化水面资产 https://item.taobao.com/item.htm?id=865360489543&pisk=gLHsX7NMiNb1gB7njFKURXly5Dwb5q9y1iZxqmBNDReTkigKDtQA6-FLJorOMSU2sqNju2VxQRoZRGDIPtWwSCmAh-yvzUJyUcman-Lyk8kOUGq4mNUAurCKv-rjPdNAXcmgnrIFkQlKjZg5RK5YDrKQpor7k-EYW9KQcuaYHPFAJ6E8JrexkSIdJuZ5MZCTHkKQju5OBoeYvMEaVtBtk-KIvyqxnDXQDCZ-fHz1qUDkpeKgJtBxdlNpTcUKrr-4i5TjbyXvy1Zg1coTRtBxKXkPllz9ZMz0QPDbfqvlIRFjTAVoQeBsl20KBSDp8B00QuV-MD-lTPDIQvFsCU1uCX3nd5DOWhqtsqDbQx71hDMb57HTOKx0CDEYdbneq6zidAkL1AT5ZPMiDjPItNTUuboEBWgJ89g0GXkgZA8dC4IyzTz7sJ5fA7XbAz-BAsf0xzPwqJ_edaNTxlTyAH_Gi5E3AmtBAsDg6kqF6HtCWc1..&spm=a21xtw.29178619.product_shelf.74.41727d6dRCDvul
|
||||
25. [ ] 添加Debug View https://zhuanlan.zhihu.com/p/668782106
|
||||
1. [ ] UE5.5 自定义/扩展缓冲可视化调试功能 https://zhuanlan.zhihu.com/p/16314329507
|
||||
26. [ ] [_UE5_ Shader Print系统](https://zhuanlan.zhihu.com/p/637929634)
|
||||
27. [ ] GBufferView实现。
|
||||
28. [ ] Toon Debug模式,可以让美术在材质进行进行简单的光照计算。
|
||||
29. [ ] ToonShadow
|
||||
1. ![[星穹铁道中下巴阴影处理.png]]
|
||||
2. [ ] ToonSDFShadow
|
||||
1. [ ] TODO: SDF贴图工具?
|
||||
30. [ ] LookDev场景
|
||||
1. [ ] https://zhuanlan.zhihu.com/p/394608910
|
||||
31. [ ] 考虑往GBuffer中添加更多数据(考虑Velocity以及SingleLayerWater)
|
||||
1. ShaderMaterialDerivedHelpers.cpp(Shader宏)、GBufferInfo.cpp(GBuffer格式)BasePassRendering.cpp(950行,SingleLayerWater写入GBuffer格式相关)
|
||||
2. 确定一下SingleLayerWater中VSMFiter与DistanceFieldShadow对渲染结果的影响,之后在文档中说明。
|
||||
32. [ ] 修复SIngleLayerWater的缩略图渲染渲染错误(双击会有一瞬间的错误产生)
|
||||
33. [ ] 添加对应的Stat https://zhuanlan.zhihu.com/p/716644594
|
||||
34. [ ] ToonLumen、GI以及晕染效果实现。![[卡通渲染晕染效果.mp4]]
|
||||
35. [ ] 在材质中实现ToonEye相关效果
|
||||
1. 【二次元人物眼睛如何变形?】 https://www.bilibili.com/video/BV14M4m1y71A/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
1. 原视频 https://www.youtube.com/watch?v=euIyX9v8rvw
|
||||
2. 眼睛建模 https://youtu.be/s2_7Q2IIvNY?si=fWiYjqcLFXzdeQ-B&t=126
|
||||
36. 尝试实现Forward+
|
||||
1. BasePass https://zhuanlan.zhihu.com/p/618698467
|
||||
37. DX11问题修复
|
||||
1. [x] ToonOutline SceneColorTexture为空的问题。 **DX11限制,必须CopyTexture**
|
||||
38. 卡通渲染针对TAA的优化思路 https://zhuanlan.zhihu.com/p/678876237
|
||||
1. https://www.bilibili.com/video/BV1BK411v7FY/?spm_id_from=333.788&vd_source=ea6df38502a795b7533aa33b78bf1159
|
||||
2. https://zhuanlan.zhihu.com/p/20786650
|
||||
39. [ ] Unreal Engine 5.4 Scene Extension https://zhuanlan.zhihu.com/p/706268007
|
||||
1. [ ] 通过SceneExtension改进ToonObjectID,这样可以减少对应ToonBuffer的精度来存其他数据。
|
||||
2. [ ] https://www.bilibili.com/video/BV1fM4m1U7Tp/
|
||||
|
||||
TODO:
|
||||
- [ ] ShadingModels.ush 完善Hair高光、自定义高光
|
||||
- [ ] 实现对应后处理效果的ComputeShader版本。
|
||||
- [ ] ToonOutline
|
||||
- [ ] RimLighting?
|
||||
- [ ] 尝试使用新的笔记系统 https://post.smzdm.com/p/a96l0kn5/
|
||||
|
||||
|
||||
>修改标记方便后续删除备注//BlueRose Modify //BlueRose Modify End
|
||||
>// Copyright BlueRose, Inc. All Rights Reserved.
|
||||
|
||||
# 渲染功能兼容的游戏
|
||||
- [ ] 罪恶装备
|
||||
- [ ] 原神
|
||||
- [ ] 星穹铁道
|
||||
- [ ] 绝区零
|
||||
- [ ] 蓝色协议
|
||||
- [ ] 碧蓝幻想
|
||||
|
||||
# 卡通渲染引擎功能总览(按照渲染顺序进行排序)
|
||||
- GBuffer For ToonShaderModel
|
||||
- [[GBuffer&Material&BasePass]]
|
||||
- [[ToonShaderModel]]
|
||||
- ToonStandard
|
||||
- Thickly - Coated
|
||||
- 描边
|
||||
- [[描边]]
|
||||
- ![[描边#实现功能]]
|
||||
- 阴影
|
||||
-
|
||||
- 边缘光
|
||||
- 其他特性
|
||||
- [[Toon眼睛渲染]]
|
||||
- [[Toon眉毛渲染]]
|
||||
|
||||
## 待实现功能
|
||||
知乎提到的渲染功能:
|
||||
- 发尖勾线 https://zhuanlan.zhihu.com/p/405518306
|
||||
- SDF 描边
|
||||
- https://zhuanlan.zhihu.com/p/113190695
|
||||
- 屏幕空间深度边缘光 Screen Space Depth Rimlight
|
||||
- https://zhuanlan.zhihu.com/p/139290492
|
||||
- 原神实现
|
||||
- https://zhuanlan.zhihu.com/p/435005339
|
||||
- 遮挡时的模糊网点效果
|
||||
- https://zhuanlan.zhihu.com/p/370140711
|
||||

|
||||
|
||||

|
||||
|
||||
按照需求优先级进行排列:
|
||||
- [ ] Anit-Lut功能
|
||||
- [ ] Anit-ToneMapping
|
||||
- [ ] 完整的Anit-Lut功能(虚拟拍摄也会用到)
|
||||
- [ ] 描边改进
|
||||
- [ ] 后处理部分,使用蓝色协议的方案
|
||||
- [ ] MultiDraw BackFace部分
|
||||
- [ ] 绘制轮廓方法落地:本村线、SDF
|
||||
- [ ] 使用添加MeshDrawPass的方式实现比较完美的BackFace
|
||||
- [ ] ToonData与ToonWorldSettings改进
|
||||
- [ ] 增加多个SSS 预积分贴图功能
|
||||
- [ ] 将材质以外的参数都集中到ToonWorldSettings类中,以方便调节
|
||||
- [ ] 面部光影方案改进
|
||||
- [ ] 在GBuffer中添加额外面部自定义法线
|
||||
- [ ] SDF面光方案
|
||||
- [ ] 角色面部Anit-Perspective 与 手指放大效果
|
||||
- [ ] 模仿VRM4U的参数
|
||||
- [ ] 参考AnimMaker
|
||||
- [ ] https://www.patreon.com/posts/56089741
|
||||
- [ ] 后处理
|
||||
- [ ] 原神 辉光效果
|
||||
- [ ] TAA抗锯齿 => Responsive AA,解决Outline模糊问题
|
||||
- [ ] 影视级Bloom实现
|
||||
- [ ] 眉毛效果
|
||||
- [ ] 可以考虑使用TranslucencySortPriority来解决
|
||||
- [ ] 通过MeshDrawPass实现眉毛最前显示效果
|
||||
- [ ] 实现眉毛描边效果
|
||||
- [ ] 额发效果
|
||||
- [ ] 额发阴影效果(衣服阴影效果)
|
||||
- [ ] 实现半透明额发
|
||||
- [ ] 天光与间接光处理
|
||||
- [ ] 为了防止环境光把角色照出立体感,所以计算环境光时,会把法线全部看作世界空间上方向来处理。同时增加了一些参数可以进行一些定制化调整。
|
||||
- [ ] 使用顶点色与第二套UV来修改一些可以实时修改的效果
|
||||
- [ ] 实现PBR <=> Cel 卡通渲染效果的参数切换
|
||||
- [ ] 使用Kawaii插件实现柔体效果
|
||||
- [ ] 后处理边缘光落地
|
||||
- [ ] SSGI 卡通渲染适配?实现AnimMaker 中的一个效果
|
||||
|
||||
- [ ] 丝袜 https://zhuanlan.zhihu.com/p/636157482
|
||||
|
||||
[[厚涂风格研究与开发笔记]]
|
||||
- ShaderModel
|
||||
- 分阶着色
|
||||
- 二阶化:
|
||||
- 多阶化:
|
||||
- 自定义次表面(暗部)颜色
|
||||
- 颜色过渡(羽化):
|
||||
- lut:待讨论
|
||||
- 高光
|
||||
- 高光贴图
|
||||
- 高光颜色
|
||||
- 参数化高光形状
|
||||
- 多层高光
|
||||
- Shader内编辑法线
|
||||
- N=_scale * L + N
|
||||
- 描边
|
||||
- 外描边:后处理描边 、 Mesh挤出 2种方式,原神采用了Mesh挤出。
|
||||
- 内描边:后处理、SDF描边、本村线 3种方式
|
||||
- 使用Mesh基础、后处理、模型绘制。(后处理传递Id贴图)
|
||||
- 阴影
|
||||
- 自定义阴影颜色
|
||||
- 自定义阴影范围
|
||||
- 自定义阴影形状
|
||||
- 阴影过渡(sdf阴影)
|
||||
- 边缘光
|
||||
- 边缘光是否受到光照影响
|
||||
- 多层边缘光
|
||||
- 边缘光衰减(入射角度更明显 or 背光角度更明显)
|
||||
- 多光源支持
|
||||
- 根据相机角度调整模型(非必要)
|
||||
- 眼睛
|
||||
- 反射 环境反射或者matcap支持
|
||||
- 内阴影 AO实现或者画死的内阴影
|
||||
- 瞳孔 瞳孔缩放
|
||||
- 视差和效果 凹凸效果
|
||||
- 高光 自定义高光形状&位置
|
||||
- 高光流动效果
|
||||
- 头发
|
||||
- 各项异性头发
|
||||
- 高光扰动
|
||||
- 高光贴图
|
||||
- 自定义高光属性
|
||||
- 高光天使环
|
||||
- 无各项异性头发
|
||||
- 自定义高光参数
|
||||
- 高光贴图
|
||||
- 高光天使环
|
||||
- 特殊效果
|
||||
- 眉毛/睫毛不受遮挡
|
||||
- 自发光
|
||||
- 阴影内素描效果
|
||||
- 后处理效果
|
||||
- 后处理 辉光效果
|
||||
- 之后提到了SunFlare,应该是那个屏幕后处理效果。卡通渲染很依赖体积光,所以自然会有好的效果,假也没关系,假才是对的。谷歌搜SNN Filter https://www.shadertoy.com/view/MlyfWd
|
||||
- 旁边的Kuwahara应该是个类似的算法,64采样。
|
||||
|
||||
# 动画技法
|
||||
- 三维动画里的仿二维动画抽帧技法教程:https://www.bilibili.com/video/BV1154y1j7tD/?spm_id_from=333.788.recommend_more_video.8&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
136
03-UnrealEngine/卡通渲染相关资料/卡通渲染相关资料.md
Normal file
136
03-UnrealEngine/卡通渲染相关资料/卡通渲染相关资料.md
Normal file
@@ -0,0 +1,136 @@
|
||||
### 其他项目分享
|
||||
- [ ] 「生と死」を物語る陰影表現とは――『Xenoblade3(ゼノブレイド3)』のキャラクターを魅せる2灯トゥーンシェーディング、世界を描くアップサンプリング【CEDEC+KYUSHU 2022】 https://gamemakers.jp/article/2023_03_22_33475/?fbclid=IwAR0v4cetIP5nKgz1N_xT45FMaoB2ElqZHedu60ZBfhix1hxsxRn6zx5c2ak
|
||||
|
||||
### 卡通渲染总结
|
||||
---
|
||||
- [ ] 米哈游技术总监首次分享:移动端高品质卡通渲染的实现与优化方案 https://zhuanlan.zhihu.com/p/37001473
|
||||
- [ ] [Unite 2018] 米哈游:在Unity中实现高品质的卡通渲染 https://www.bilibili.com/video/BV1Df4y1X7G3/
|
||||
- [ ] 【从负一到零的卡通渲染】卡渲笔记:https://zhuanlan.zhihu.com/p/369443326
|
||||
- [ ] 【从零到零点一的原神卡渲还原】记录还原的尝试和思考 https://zhuanlan.zhihu.com/p/376094989
|
||||
- [ ] 原神角色渲染Shader分析还原 https://zhuanlan.zhihu.com/p/360229590
|
||||
- [ ] 原神图形技术简析及杂谈 https://zhuanlan.zhihu.com/p/260824391
|
||||
- [ ] 卡通渲染小记一(原神篇) https://zhuanlan.zhihu.com/p/374466780
|
||||
- [ ] [译] 崩坏3的卡通渲染实现方式拆解 https://zhuanlan.zhihu.com/p/120908528
|
||||
-
|
||||
---
|
||||
- [ ] 卡通渲染相关小结 https://zhuanlan.zhihu.com/p/83619204
|
||||
- [ ] Unity卡通渲染总结 NPR Toon Shading https://zhuanlan.zhihu.com/p/330599077
|
||||
- [ ] 卡通渲染学习总结 https://zhuanlan.zhihu.com/p/163791090
|
||||
- [ ] 【02】从零开始的卡通渲染-着色篇1 https://zhuanlan.zhihu.com/p/110025903
|
||||
- [ ] 【03】从零开始的卡通渲染-着色篇2 https://zhuanlan.zhihu.com/p/111633226
|
||||
- [ ] 【04】从零开始的卡通渲染-PBR篇 https://zhuanlan.zhihu.com/p/115238808
|
||||
- [ ] 卡通渲染-----关于常规着色效果的那点公式 https://zhuanlan.zhihu.com/p/258615988
|
||||
- [ ] Godot还原 碧蓝幻想Versus GGX 卡通渲染 https://zhuanlan.zhihu.com/p/390781757
|
||||
---
|
||||
新版罪恶装备
|
||||
- [ ] 风格化角色渲染整理之罪恶装备Strive https://zhuanlan.zhihu.com/p/384202640
|
||||
---
|
||||
- [ ] 简单谈谈原神的渲染部分 https://zhuanlan.zhihu.com/p/259589537
|
||||
- [ ] 到目前为止的二次元渲染总结 https://zhuanlan.zhihu.com/p/126668414
|
||||
- [ ] 用SDF处理卡通内描线的锯齿问题 https://zhuanlan.zhihu.com/p/113190695
|
||||
- [ ] 借AI梦境档案谈谈NPR渲染 https://zhuanlan.zhihu.com/p/87619107
|
||||
---
|
||||
- [ ] 【JTRP】Unity HDRP卡通渲染总结 https://zhuanlan.zhihu.com/p/385186333
|
||||
- [ ] 开源JTRP:Unity HDRP ToonShading Render Pipeline https://zhuanlan.zhihu.com/p/126229177
|
||||
---
|
||||
重力眩晕分析
|
||||
- [ ] 01开篇:重力眩晕2中的渲染效果概览 https://zhuanlan.zhihu.com/p/94581861
|
||||
- [ ] 02卡通渲染基本光照模型的实现 https://zhuanlan.zhihu.com/p/95986273
|
||||
- [ ] 03 卡通渲染LightMap的使用 https://zhuanlan.zhihu.com/p/97338996
|
||||
- [ ] 04 卡通渲染 次表面散射效果的简易实现 https://zhuanlan.zhihu.com/p/97892884
|
||||
|
||||
---
|
||||
|
||||
### 描边
|
||||
- [ ] High-Quality Real-Time Outline Rendering 描边学习记录 https://zhuanlan.zhihu.com/p/318769754
|
||||
- [ ] 在shader中实现五种描边方法
|
||||
- [ ] 【01】从零开始的卡通渲染-描边篇 https://zhuanlan.zhihu.com/p/109101851
|
||||
- [ ] GPU Pro 1 使用几何着色器的NPR效果 | NPR Effects Using the Geometry Shader——使用几何着色器进行描边
|
||||
- [ ] 在UE4引擎中做卡通描边的一点心得 https://zhuanlan.zhihu.com/p/234535777
|
||||
- [ ] 修复发尖的描边法线 https://zhuanlan.zhihu.com/p/405518306
|
||||
|
||||
### 眼睛头发面部阴影
|
||||
- [ ] 神作面部阴影渲染还原 https://zhuanlan.zhihu.com/p/279334552
|
||||
- [ ] 二次元角色卡通渲染—面部篇 https://zhuanlan.zhihu.com/p/411188212
|
||||
- [ ] 二次元角色卡通渲染—眼睛篇 https://zhuanlan.zhihu.com/p/402861632
|
||||
- [ ] Unity URP】以Render Feature实现卡通渲染中的刘海投影 https://zhuanlan.zhihu.com/p/232450616
|
||||
|
||||
### 场景
|
||||
- [ ] UE4 吉卜力 风格化shader ~ 树 https://zhuanlan.zhihu.com/p/402180364
|
||||
- [ ] [游戏美术文档]UE4风格化草地 https://zhuanlan.zhihu.com/p/355609504
|
||||
- [ ] 虚幻4重现“哈尔的移动城堡”花园(附风格化草地制作分享)https://zhuanlan.zhihu.com/p/272734944
|
||||
- [ ] 虚幻4还原风格化“云海群山”场景(附岩石贴图制作思路)https://zhuanlan.zhihu.com/p/337054665
|
||||
- [ ] UE4中用Niagara实现procedural浪花 https://zhuanlan.zhihu.com/p/100700549
|
||||
- [ ] Kuwahara 滤波UE4卡通渲染基础教程 Part4:Paint Filter https://zhuanlan.zhihu.com/p/74807856
|
||||
- [ ] 卡通风格场景的定制化技术部分 https://zhuanlan.zhihu.com/p/338334377
|
||||
- [ ] PBR光照体系下的卡通渲染光照模型 https://zhuanlan.zhihu.com/p/166147653
|
||||
- [ ] 【翻译】在UE4 中创建风格化的丛林环境 https://zhuanlan.zhihu.com/p/261083501
|
||||
|
||||
### 国外文章翻译
|
||||
- [ ] Cygames21年11月技术分享(上篇)https://zhuanlan.zhihu.com/p/441159789
|
||||
- [ ] 「细节到眼泪、眼睛、汗水,赛马娘3D角色模型技术分享」——Cygames21年11月技术分享(下篇https://zhuanlan.zhihu.com/p/441560356
|
||||
- [ ] 【翻译】NPR角色渲染总结!现在是迈向更高境界之时:https://zhuanlan.zhihu.com/p/345915866
|
||||
- [ ] [简单机翻/多图]《使《蓝色协议》成为“剧场动画品质”的手法是?》https://zhuanlan.zhihu.com/p/339603529
|
||||
- [ ] 翻译《使用UE4开发圣剑传说3的经验分享•角色渲染部分》+效果实现 https://zhuanlan.zhihu.com/p/360587048
|
||||
- [ ] 卡通渲染手游七大罪的技术介绍,一 https://zhuanlan.zhihu.com/p/161326626
|
||||
|
||||
### 其他技术
|
||||
- [ ] 「黑丝」的材质如何用shader实现?数学模型是怎样的?https://www.zhihu.com/question/35094847/answer/61161188
|
||||
- [ ] 遮挡时的模糊网点效果 https://zhuanlan.zhihu.com/p/370140711
|
||||
- [ ] 《Honey Select》捏人剖析 https://zhuanlan.zhihu.com/p/28471808
|
||||
## 参考项目
|
||||
- 《莱莎的炼金工坊12》
|
||||
- 《破晓传说》
|
||||
- 《蓝色协议》https://zhuanlan.zhihu.com/p/229621134?tdsourcetag=s_pctim_aiomsg
|
||||
- 《少女前线2》
|
||||
- 《明日方舟:终末地》
|
||||
- 《kurtzpel》
|
||||
- 《守望先锋》
|
||||
- 《二之国》
|
||||
- 原神掰法线 https://www.bilibili.com/video/BV1Nr4y1K7Wc
|
||||
|
||||
## ue4与u3d参考项目
|
||||
- [x] UnityChanToonShader v1、v2、URP
|
||||
- [x] UnityURPToonLitShaderExample:https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample
|
||||
- [x] JTRP:https://github.com/Jason-Ma-233/JasonMaToonRenderPipeline
|
||||
- [x] VRM4U
|
||||
|
||||
## 抓帧工具
|
||||
- 使用 Intel GPA 与 分析3D程序和抓取模型:https://www.cnblogs.com/TracePlus/p/4233606.html
|
||||
|
||||
## u3d资料
|
||||
- 走近卡通渲染——关于Trick的二三事https://learn.u3d.cn/tutorial/cel-shading-trick
|
||||
- 卡通渲染——风格和影视化探索 https://learn.u3d.cn/tutorial/cel-shading-cinematics
|
||||
- 【Unite Tokyo 2018】ToonShader对话环节#1『RealtimeToonShader彻谈』Part2:https://matrix64.github.io/Unite18ToonShader_p2-post/
|
||||
- 〈七つの大罪〉をゲームで!高品質グラフィックを具現化するための技法と開発最適化のご紹介 – Unity Learning Materials:https://learning.unity3d.jp/3227/
|
||||
- スマホVTuber向け揺れモノシステムを「ユニティちゃんライセンス」で無料公開! – Unity Learning Materials:https://learning.unity3d.jp/668/
|
||||
- (466) 【Unite 2017 Tokyo】VR MAGIC! ~キャラクターに命を吹き込んだこの4年間の記録~ - YouTube:https://www.youtube.com/watch?v=nWR816af2dU&feature=youtu.be
|
||||
- Unity Chan研究(3)思维脑图 - 知乎:https://zhuanlan.zhihu.com/p/105047326
|
||||
- (466) 【Unite 2017 Tokyo】Unityで楽しむノンフォトリアルな絵づくり講座:トゥーンシェーダー・マニアクス - YouTube:https://www.youtube.com/watch?v=6aNB9LhSx7g
|
||||
- 【Unite 2017 Tokyo】Unityで楽しむノンフォトリアルな絵づくり講座:トゥーンシェーダー・マニアクス:https://www.slideshare.net/Unite2017Tokyo/unite-2017-tokyounity-75800622
|
||||
|
||||
## UE4资料
|
||||
- 关于matcap材质的效果优化 - 知乎:https://zhuanlan.zhihu.com/p/57775690
|
||||
- UE4_Ramp生成器_仿原神皮肤shader:https://zhuanlan.zhihu.com/p/248998437
|
||||
- UE4-Niagara与材质在二次元的新展开:http://asher.gg/blog/wp-content/uploads/2019/11/UE4-Niagara%E4%B8%8E%E6%9D%90%E8%B4%A8%E5%9C%A8%E4%BA%8C%E6%AC%A1%E5%85%83%E7%9A%84%E6%96%B0%E5%B1%95%E5%BC%80.pdf
|
||||
- UnrealEngine4】从虚幻四的着色模型到NPR:https://zhuanlan.zhihu.com/p/30965016
|
||||
|
||||
### UE4场景
|
||||
百度网盘:
|
||||
- 风格化场景包
|
||||
- 仿原神蒙德场景:Stylized Medieval Village 4.26
|
||||
- 吉普力风格场景:Stylized Village by Meshingun Studio 4.23-4.26.zip https://mega.nz/folder/iiBCiRpa#B0aaIOv32tJhybTe9gRZXg
|
||||
- 比较烂的 stylized-colorful-camp-pack https://www.bilibili.com/video/BV1Gv411T77m?spm_id_from=333.851.dynamic.content.click
|
||||
|
||||
## 其他资料
|
||||
- 知乎收藏夹
|
||||
- GUILTY GEAR Xrd REVELATOR 3D进化出的非照片真实视觉 https://www.cnblogs.com/TracePlus/p/5697705.html
|
||||
- 【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(1):https://www.cnblogs.com/TracePlus/p/4205798.html
|
||||
- 【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(2):https://www.cnblogs.com/TracePlus/p/4205834.html
|
||||
- 【翻译】西川善司的「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,后篇:https://www.cnblogs.com/TracePlus/p/4205978.html
|
||||
- artstation市场:https://www.artstation.com/marketplace/p/9nrrR/genshin-impact-character-shader-for-eevee
|
||||
|
||||
风蚀之月Blog
|
||||
- UE4-Fest-圣剑传说3笔记:https://blog.ch-wind.com/unreal-fest-trials-of-mana-3-note/
|
||||
- UE4-Fest-NPR笔记:https://blog.ch-wind.com/ue4-fest-npr-note/
|
||||
- CEDEC2020-新樱花大战笔记(Toon风格的PBR):https://blog.ch-wind.com/cedec2020-new-sakura-wars-note/
|
504
03-UnrealEngine/卡通渲染相关资料/渲染功能/PostProcess/ToonPostProcess.md
Normal file
504
03-UnrealEngine/卡通渲染相关资料/渲染功能/PostProcess/ToonPostProcess.md
Normal file
@@ -0,0 +1,504 @@
|
||||
---
|
||||
title: ToonPostProcess
|
||||
date: 2024-05-15 16:50:13
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# FFT
|
||||
|
||||
# Bloom
|
||||
Bloom主要分
|
||||
- Bloom
|
||||
- FFTBloom
|
||||
- LensFlares
|
||||
|
||||
|
||||
BloomThreshold,ClampMin = "-1.0", UIMax = "8.0"。
|
||||
相关逻辑位于:
|
||||
```c++
|
||||
if (bBloomSetupRequiredEnabled)
|
||||
{
|
||||
const float BloomThreshold = View.FinalPostProcessSettings.BloomThreshold;
|
||||
|
||||
FBloomSetupInputs SetupPassInputs;
|
||||
SetupPassInputs.SceneColor = DownsampleInput;
|
||||
SetupPassInputs.EyeAdaptationBuffer = EyeAdaptationBuffer;
|
||||
SetupPassInputs.EyeAdaptationParameters = &EyeAdaptationParameters;
|
||||
SetupPassInputs.LocalExposureParameters = &LocalExposureParameters;
|
||||
SetupPassInputs.LocalExposureTexture = CVarBloomApplyLocalExposure.GetValueOnRenderThread() ? LocalExposureTexture : nullptr;
|
||||
SetupPassInputs.BlurredLogLuminanceTexture = LocalExposureBlurredLogLumTexture;
|
||||
SetupPassInputs.Threshold = BloomThreshold;
|
||||
SetupPassInputs.ToonThreshold = View.FinalPostProcessSettings.ToonBloomThreshold;
|
||||
|
||||
DownsampleInput = AddBloomSetupPass(GraphBuilder, View, SetupPassInputs);
|
||||
}
|
||||
```
|
||||
## FFTBloom
|
||||
***普通Bloom算法只能做到圆形光斑,对于自定义形状的就需要使用FFTBloom。***
|
||||
|
||||
- FFT Bloom:https://zhuanlan.zhihu.com/p/611582936
|
||||
- Unity FFT Bloom:https://github.com/AKGWSB/FFTConvolutionBloom
|
||||
|
||||
### 频域与卷积定理
|
||||
图像可以视为二维的信号,而一个信号可以通过 **不同频率** 的 Sine & Cosine 函数的线性叠加来近似得到。对于每个频率的函数,我们乘以一个常数振幅并叠加到最终的结果上,这些振幅叫做 **频谱**。值得注意的是所有的 F_k 都是 **复数**:
|
||||
|
||||

|
||||
此时频域上的每个振幅不再代表某个单个的时域样本,而是代表该频段的 Sine & Cosine 函数对时域信号的 **整体** 贡献。频域信号包含了输入图像的全部时域信息,***因此卷积定理告诉我们在时域上对信号做卷积,等同于将源图像与滤波盒图像在频域上的频谱(上图系数 V_k)做简单复数 **乘法***:
|
||||

|
||||
一一对位的乘法速度是远远快于需要循环累加的朴素卷积操作。因此接下来我们的目标就是找到一种方法,建立图像信号与其频域之间的联系。在通信领域通常使用傅里叶变换来进行信号的频、时域转换
|
||||
|
||||
### 相关代码
|
||||
- c++
|
||||
- AddFFTBloomPass()
|
||||
- FBloomFinalizeApplyConstantsCS (Bloom计算完成)
|
||||
- AddTonemapPass(),PassInputs.Bloom = Bloom与PassInputs.SceneColorApplyParamaters
|
||||
- Shader
|
||||
-
|
||||
|
||||
**FBloomFindKernelCenterCS**:用于找到Bloom效果的核(Kernel)中心(纹理中找到最亮的像素)。用于在一个,并记录其位置。主要通过计算Luminance来获取到中心区域,而在这里的中心区域可以有多个,这也代表着在最终输出的SceneColor里可以有多个【曝点光晕(Bloom)效果】
|
||||
|
||||
|
||||
|
||||
# 实用代码
|
||||
代码位于DeferredShadingCommon.ush:
|
||||
```c++
|
||||
// @param UV - UV space in the GBuffer textures (BufferSize resolution)
|
||||
FGBufferData GetGBufferData(float2 UV, bool bGetNormalizedNormal = true)
|
||||
{
|
||||
#if GBUFFER_REFACTOR
|
||||
return DecodeGBufferDataUV(UV,bGetNormalizedNormal);
|
||||
#else
|
||||
float4 GBufferA = Texture2DSampleLevel(SceneTexturesStruct.GBufferATexture, SceneTexturesStruct_GBufferATextureSampler, UV, 0);
|
||||
float4 GBufferB = Texture2DSampleLevel(SceneTexturesStruct.GBufferBTexture, SceneTexturesStruct_GBufferBTextureSampler, UV, 0);
|
||||
float4 GBufferC = Texture2DSampleLevel(SceneTexturesStruct.GBufferCTexture, SceneTexturesStruct_GBufferCTextureSampler, UV, 0);
|
||||
float4 GBufferD = Texture2DSampleLevel(SceneTexturesStruct.GBufferDTexture, SceneTexturesStruct_GBufferDTextureSampler, UV, 0);
|
||||
float CustomNativeDepth = Texture2DSampleLevel(SceneTexturesStruct.CustomDepthTexture, SceneTexturesStruct_CustomDepthTextureSampler, UV, 0).r;
|
||||
|
||||
// BufferToSceneTextureScale is necessary when translucent materials are rendered in a render target
|
||||
// that has a different resolution than the scene color textures, e.g. r.SeparateTranslucencyScreenPercentage < 100.
|
||||
int2 IntUV = (int2)trunc(UV * View.BufferSizeAndInvSize.xy * View.BufferToSceneTextureScale.xy);
|
||||
uint CustomStencil = SceneTexturesStruct.CustomStencilTexture.Load(int3(IntUV, 0)) STENCIL_COMPONENT_SWIZZLE;
|
||||
|
||||
#if ALLOW_STATIC_LIGHTING
|
||||
float4 GBufferE = Texture2DSampleLevel(SceneTexturesStruct.GBufferETexture, SceneTexturesStruct_GBufferETextureSampler, UV, 0);
|
||||
#else
|
||||
float4 GBufferE = 1;
|
||||
#endif
|
||||
|
||||
float4 GBufferF = Texture2DSampleLevel(SceneTexturesStruct.GBufferFTexture, SceneTexturesStruct_GBufferFTextureSampler, UV, 0);
|
||||
|
||||
#if WRITES_VELOCITY_TO_GBUFFER
|
||||
float4 GBufferVelocity = Texture2DSampleLevel(SceneTexturesStruct.GBufferVelocityTexture, SceneTexturesStruct_GBufferVelocityTextureSampler, UV, 0);
|
||||
#else
|
||||
float4 GBufferVelocity = 0;
|
||||
#endif
|
||||
|
||||
float SceneDepth = CalcSceneDepth(UV);
|
||||
|
||||
return DecodeGBufferData(GBufferA, GBufferB, GBufferC, GBufferD, GBufferE, GBufferF, GBufferVelocity, CustomNativeDepth, CustomStencil, SceneDepth, bGetNormalizedNormal, CheckerFromSceneColorUV(UV));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Minimal path for just the lighting model, used to branch around unlit pixels (skybox)
|
||||
uint GetShadingModelId(float2 UV)
|
||||
{
|
||||
return DecodeShadingModelId(Texture2DSampleLevel(SceneTexturesStruct.GBufferBTexture, SceneTexturesStruct_GBufferBTextureSampler, UV, 0).a);
|
||||
}
|
||||
```
|
||||
|
||||
## ShadingModel判断
|
||||
```c++
|
||||
bool IsToonShadingModel(float2 UV)
|
||||
{
|
||||
uint ShadingModel = DecodeShadingModelId(Texture2DSampleLevel(SceneTexturesStruct.GBufferBTexture, SceneTexturesStruct_GBufferBTextureSampler, UV, 0).a);
|
||||
return ShadingModel == SHADINGMODELID_TOONSTANDARD
|
||||
|| ShadingModel == SHADINGMODELID_PREINTEGRATED_SKIN;
|
||||
}
|
||||
```
|
||||
PS.需要Shader添加FSceneTextureShaderParameters/FSceneTextureUniformParameters。
|
||||
```c++
|
||||
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FSceneTextureUniformParameters, "SceneTexturesStruct", SceneTextures);
|
||||
|
||||
BEGIN_SHADER_PARAMETER_STRUCT(FSceneTextureShaderParameters, ENGINE_API)
|
||||
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
|
||||
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FMobileSceneTextureUniformParameters, MobileSceneTextures)
|
||||
END_SHADER_PARAMETER_STRUCT()
|
||||
```
|
||||
|
||||
|
||||
|
||||
# ToneMapping
|
||||
- UE5 官方文档High Dynamic Range Display Output:https://dev.epicgames.com/documentation/en-us/unreal-engine/high-dynamic-range-display-output-in-unreal-engine?application_version=5.3
|
||||
- ACES官方文档:https://docs.acescentral.com/#aces-white-point-derivation
|
||||
- https://modelviewer.dev/examples/tone-mapping
|
||||
- [现代游戏图形中的sRGB18%灰-中性灰的定义](https://zhuanlan.zhihu.com/p/654557489)
|
||||
- 游戏中的后处理(三):渲染流水线、ACES、Tonemapping和 HDR:https://zhuanlan.zhihu.com/p/118272193
|
||||
|
||||
## ACES 色彩空间
|
||||
ACES 标准定义了一些色域和色彩空间如下:
|
||||
|
||||
色域有:
|
||||
- AP0,包含所有颜色的色域
|
||||
- AP1,工作色域
|
||||
|
||||
色彩空间有:
|
||||
- ACES2065-1/ACES 色彩空间,使用 AP0 色域,用于存储颜色,处理色彩转换
|
||||
- ACEScg 色彩空间,使用 AP1 色域,一个线性的渲染计算工作空间
|
||||
- ACEScc 色彩空间,AP1 色域,指数空间,用于调色
|
||||
- ACEScct 色彩空间,使用 AP1 色域,和 ACEScc 类似,只是曲线略有不同,适用于不同的场景
|
||||
|
||||
## UE5的ACES流程
|
||||
ACES Viewing Transform在查看流程中将按以下顺序进行:
|
||||
|
||||
- **Look Modification Transform (LMT)** - 这部分抓取应用了创意"外观"(颜色分级和矫正)的ACES颜色编码图像, 输出由ACES和Reference Rendering Transform(RRT)及Output Device Transform(ODT)渲染的图像。
|
||||
- **Reference Rendering Transform (RRT)** - 之后,这部分抓取参考场景的颜色值,将它们转换为参考显示。 在此流程中,它使渲染图像不再依赖于特定显示器,反而能保证它输出到特定显示器时拥有正确而宽泛的色域和动态范围(尚未创建的图像同样如此)。
|
||||
- **Output Device Transform (ODT)** - 最后,这部分抓取RRT的HDR数据输出,将其与它们能够显示的不同设备和色彩空间进行比对。 因此,每个目标需要将其自身的ODT与Rec709、Rec2020、DCI-P3等进行比对。
|
||||
|
||||
## ToneMapping种类
|
||||
- ShaderToy效果演示:
|
||||
- https://www.shadertoy.com/view/McG3WW
|
||||
- ACES
|
||||
- Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
|
||||
- https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
|
||||
- PBR Neutral https://modelviewer.dev/examples/tone-mapping
|
||||
- Uncharted tonemapping
|
||||
- http://filmicworlds.com/blog/filmic-tonemapping-operators/
|
||||
- https://www.gdcvault.com/play/1012351/Uncharted-2-HDR
|
||||
- AgX
|
||||
- https://github.com/sobotka/AgX
|
||||
- https://www.shadertoy.com/view/cd3XWr
|
||||
- https://www.shadertoy.com/view/lslGzl
|
||||
- https://www.shadertoy.com/view/Xstyzn
|
||||
- GT-ToneMapping:https://github.com/yaoling1997/GT-ToneMapping
|
||||
- 曲线:https://www.desmos.com/calculator/gslcdxvipg?lang=zh-CN
|
||||
- CCA-ToneMapping:?
|
||||
## UE中的相关实现
|
||||
UE4版本的笔记:[[UE4 ToneMapping]]
|
||||
|
||||
TonemapCommon.ush中的FilmToneMap()在CombineLUTsCommon()中调用。其顺序为:
|
||||
1. AddCombineLUTPass() => PostProcessCombineLUTs.usf
|
||||
2. AddTonemapPass() => PostProcessTonemap.usf
|
||||
|
||||
```c++
|
||||
void AddPostProcessingPasses()
|
||||
{
|
||||
...
|
||||
{
|
||||
FRDGTextureRef ColorGradingTexture = nullptr;
|
||||
|
||||
if (bPrimaryView)
|
||||
{
|
||||
ColorGradingTexture = AddCombineLUTPass(GraphBuilder, View);
|
||||
}
|
||||
// We can re-use the color grading texture from the primary view.
|
||||
else if (View.GetTonemappingLUT())
|
||||
{
|
||||
ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, View.GetTonemappingLUT());
|
||||
}
|
||||
else
|
||||
{
|
||||
const FViewInfo* PrimaryView = static_cast<const FViewInfo*>(View.Family->Views[0]);
|
||||
ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, PrimaryView->GetTonemappingLUT());
|
||||
}
|
||||
|
||||
FTonemapInputs PassInputs;
|
||||
PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, PassInputs.OverrideOutput);
|
||||
PassInputs.SceneColor = SceneColorSlice;
|
||||
PassInputs.Bloom = Bloom;
|
||||
PassInputs.SceneColorApplyParamaters = SceneColorApplyParameters;
|
||||
PassInputs.LocalExposureTexture = LocalExposureTexture;
|
||||
PassInputs.BlurredLogLuminanceTexture = LocalExposureBlurredLogLumTexture;
|
||||
PassInputs.LocalExposureParameters = &LocalExposureParameters;
|
||||
PassInputs.EyeAdaptationParameters = &EyeAdaptationParameters;
|
||||
PassInputs.EyeAdaptationBuffer = EyeAdaptationBuffer;
|
||||
PassInputs.ColorGradingTexture = ColorGradingTexture;
|
||||
PassInputs.bWriteAlphaChannel = AntiAliasingMethod == AAM_FXAA || bProcessSceneColorAlpha;
|
||||
PassInputs.bOutputInHDR = bTonemapOutputInHDR;
|
||||
|
||||
SceneColor = AddTonemapPass(GraphBuilder, View, PassInputs);
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
|
||||
CommonPassParameters.SceneTextures = SceneTextures.GetSceneTextureShaderParameters(View.FeatureLevel);
|
||||
## PostProcessCombineLUTs.usf
|
||||
相关变量更新函数位于FCachedLUTSettings::GetCombineLUTParameters()
|
||||
|
||||
## PostProcessTonemap.usf
|
||||
|
||||
## 实现方法
|
||||
|
||||
```c++
|
||||
//BlueRose Modify
|
||||
FGBufferData SamplerBuffer = GetGBufferData(UV * View.ResolutionFractionAndInv.x, false);
|
||||
if (SamplerBuffer.CustomStencil > 1.0f && abs(SamplerBuffer.CustomDepth - SamplerBuffer.Depth) < 1)
|
||||
{
|
||||
// OutColor = SampleSceneColor(UV);
|
||||
OutColor = TonemapCommonPS(UV, InVignette, GrainUV, ScreenPos, FullViewUV, SvPosition, Luminance);
|
||||
}else
|
||||
{
|
||||
OutColor = TonemapCommonPS(UV, InVignette, GrainUV, ScreenPos, FullViewUV, SvPosition, Luminance);
|
||||
}
|
||||
//BlueRose Modify End
|
||||
```
|
||||
|
||||
### TextureArray参考
|
||||
FIESAtlasAddTextureCS::FParameters:
|
||||
- SHADER_PARAMETER_TEXTURE_ARRAY
|
||||
- /Engine/Private/IESAtlas.usf
|
||||
|
||||
```c++
|
||||
static void AddSlotsPassCS(
|
||||
FRDGBuilder& GraphBuilder,
|
||||
FGlobalShaderMap* ShaderMap,
|
||||
const TArray<FAtlasSlot>& Slots,
|
||||
FRDGTextureRef& OutAtlas)
|
||||
{
|
||||
FRDGTextureUAVRef AtlasTextureUAV = GraphBuilder.CreateUAV(OutAtlas);
|
||||
TShaderMapRef<FIESAtlasAddTextureCS> ComputeShader(ShaderMap);
|
||||
|
||||
// Batch new slots into several passes
|
||||
const uint32 SlotCountPerPass = 8u;
|
||||
const uint32 PassCount = FMath::DivideAndRoundUp(uint32(Slots.Num()), SlotCountPerPass);
|
||||
for (uint32 PassIt = 0; PassIt < PassCount; ++PassIt)
|
||||
{
|
||||
const uint32 SlotOffset = PassIt * SlotCountPerPass;
|
||||
const uint32 SlotCount = SlotCountPerPass * (PassIt+1) <= uint32(Slots.Num()) ? SlotCountPerPass : uint32(Slots.Num()) - (SlotCountPerPass * PassIt);
|
||||
|
||||
FIESAtlasAddTextureCS::FParameters* Parameters = GraphBuilder.AllocParameters<FIESAtlasAddTextureCS::FParameters>();
|
||||
Parameters->OutAtlasTexture = AtlasTextureUAV;
|
||||
Parameters->AtlasResolution = OutAtlas->Desc.Extent;
|
||||
Parameters->AtlasSliceCount = OutAtlas->Desc.ArraySize;
|
||||
Parameters->ValidCount = SlotCount;
|
||||
for (uint32 SlotIt = 0; SlotIt < SlotCountPerPass; ++SlotIt)
|
||||
{
|
||||
Parameters->InTexture[SlotIt] = GSystemTextures.BlackDummy->GetRHI();
|
||||
Parameters->InSliceIndex[SlotIt].X = InvalidSlotIndex;
|
||||
Parameters->InSampler[SlotIt] = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
|
||||
}
|
||||
for (uint32 SlotIt = 0; SlotIt<SlotCount;++SlotIt)
|
||||
{
|
||||
const FAtlasSlot& Slot = Slots[SlotOffset + SlotIt];
|
||||
check(Slot.SourceTexture);
|
||||
Parameters->InTexture[SlotIt] = Slot.GetTextureRHI();
|
||||
Parameters->InSliceIndex[SlotIt].X = Slot.SliceIndex;
|
||||
}
|
||||
|
||||
const FIntVector DispatchCount = FComputeShaderUtils::GetGroupCount(FIntVector(Parameters->AtlasResolution.X, Parameters->AtlasResolution.Y, SlotCount), FIntVector(8, 8, 1));
|
||||
FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("IESAtlas::AddTexture"), ComputeShader, Parameters, DispatchCount);
|
||||
}
|
||||
|
||||
GraphBuilder.UseExternalAccessMode(OutAtlas, ERHIAccess::SRVMask);
|
||||
}
|
||||
```
|
||||
|
||||
```c++
|
||||
Texture2D<float4> InTexture_0;
|
||||
Texture2D<float4> InTexture_1;
|
||||
Texture2D<float4> InTexture_2;
|
||||
Texture2D<float4> InTexture_3;
|
||||
Texture2D<float4> InTexture_4;
|
||||
Texture2D<float4> InTexture_5;
|
||||
Texture2D<float4> InTexture_6;
|
||||
Texture2D<float4> InTexture_7;
|
||||
|
||||
uint4 InSliceIndex[8];
|
||||
|
||||
SamplerState InSampler_0;
|
||||
SamplerState InSampler_1;
|
||||
SamplerState InSampler_2;
|
||||
SamplerState InSampler_3;
|
||||
SamplerState InSampler_4;
|
||||
SamplerState InSampler_5;
|
||||
SamplerState InSampler_6;
|
||||
SamplerState InSampler_7;
|
||||
|
||||
int2 AtlasResolution;
|
||||
uint AtlasSliceCount;
|
||||
uint ValidCount;
|
||||
|
||||
RWTexture2DArray<float> OutAtlasTexture;
|
||||
|
||||
[numthreads(8, 8, 1)]
|
||||
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
{
|
||||
if (all(DispatchThreadId.xy < uint2(AtlasResolution)))
|
||||
{
|
||||
const uint2 DstPixelPos = DispatchThreadId.xy;
|
||||
uint DstSlice = 0;
|
||||
|
||||
const float2 SrcUV = (DstPixelPos + 0.5) / float2(AtlasResolution);
|
||||
const uint SrcSlice = DispatchThreadId.z;
|
||||
|
||||
if (SrcSlice < ValidCount)
|
||||
{
|
||||
float Color = 0;
|
||||
switch (SrcSlice)
|
||||
{
|
||||
case 0: Color = InTexture_0.SampleLevel(InSampler_0, SrcUV, 0).x; DstSlice = InSliceIndex[0].x; break;
|
||||
case 1: Color = InTexture_1.SampleLevel(InSampler_1, SrcUV, 0).x; DstSlice = InSliceIndex[1].x; break;
|
||||
case 2: Color = InTexture_2.SampleLevel(InSampler_2, SrcUV, 0).x; DstSlice = InSliceIndex[2].x; break;
|
||||
case 3: Color = InTexture_3.SampleLevel(InSampler_3, SrcUV, 0).x; DstSlice = InSliceIndex[3].x; break;
|
||||
case 4: Color = InTexture_4.SampleLevel(InSampler_4, SrcUV, 0).x; DstSlice = InSliceIndex[4].x; break;
|
||||
case 5: Color = InTexture_5.SampleLevel(InSampler_5, SrcUV, 0).x; DstSlice = InSliceIndex[5].x; break;
|
||||
case 6: Color = InTexture_6.SampleLevel(InSampler_6, SrcUV, 0).x; DstSlice = InSliceIndex[6].x; break;
|
||||
case 7: Color = InTexture_7.SampleLevel(InSampler_7, SrcUV, 0).x; DstSlice = InSliceIndex[7].x; break;
|
||||
}
|
||||
|
||||
// Ensure there is no NaN value
|
||||
Color = -min(-Color, 0);
|
||||
|
||||
DstSlice = min(DstSlice, AtlasSliceCount-1);
|
||||
OutAtlasTexture[uint3(DstPixelPos, DstSlice)] = Color;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ToneMapping Method更换
|
||||
```c++
|
||||
// Tonemapped color in the AP1 gamut
|
||||
float3 ToneMappedColorAP1 = FilmToneMap( ColorAP1 );
|
||||
// float3 ToneMappedColorAP1 = ColorAP1;
|
||||
// float3 ToneMappedColorAP1 = AGXToneMap(ColorAP1);
|
||||
// float3 ToneMappedColorAP1 = GTToneMap(ColorAP1);
|
||||
// float3 ToneMappedColorAP1 = PBRNeutralToneMap(ColorAP1);
|
||||
```
|
||||
|
||||
# UE5 PostProcess 添加Pass代码
|
||||
- UE4中添加自定义ComputerShader:https://zhuanlan.zhihu.com/p/413884878
|
||||
- UE渲染学习(2)- 自定义PostProcess - Kuwahara滤镜:https://zhuanlan.zhihu.com/p/25790491262
|
||||
|
||||
# 在UE5添加自定义后处理Pass的方法
|
||||
## 在if(bPostProcessingEnabled)代码段
|
||||
1. 在AddPostProcessingPasses()(PostProcessing.cpp)中的**EPass**与**PassNames**数组中添加Pass名称枚举与Pass名称字符串。
|
||||
2. 在**PassSequence.Finalize()** 之前,添加上一步EPass同名的`PassSequence.SetEnabled(EPass::XXX)` 。该逻辑用于控制对应Pass是否起作用。
|
||||
3. 根据步骤1中新增Pass的顺序,在对应的位置添加对应的代码段。额外需要注意的Tonemap前后的Pass代码有所不同。
|
||||
|
||||
### Pass代码
|
||||
1. `MotionBlur`之前(后处理材质BL_BeforeTonemapping之前),传入FScreenPassTexture SceneColor进行绘制。
|
||||
2. `MotionBlur`~`Tonemap`传入FScreenPassTextureSlice SceneColorSlice进行绘制。
|
||||
3. `Tonemap`之后,传入FScreenPassTexture SceneColor进行绘制。
|
||||
|
||||
`MotionBlur`~`Tonemap`大致这么写:
|
||||
```c++
|
||||
if (PassSequence.IsEnabled(EPass::XXX))
|
||||
{
|
||||
FXXXInputs PassInputs;
|
||||
PassSequence.AcceptOverrideIfLastPass(EPass::XXX, PassInputs.OverrideOutput);
|
||||
PassInputs.SceneColorSlice = SceneColorSlice;
|
||||
|
||||
SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddXXXPass(GraphBuilder, View, PassInputs));
|
||||
}
|
||||
```
|
||||
|
||||
其他位置大致这么写:
|
||||
```c++
|
||||
if (PassSequence.IsEnabled(EPass::XXX))
|
||||
{
|
||||
FXXXInputs PassInputs;
|
||||
PassSequence.AcceptOverrideIfLastPass(EPass::XXX, PassInputs.OverrideOutput);
|
||||
PassInputs.SceneColor = SceneColor;
|
||||
|
||||
SceneColor = AddXXXPass(GraphBuilder, View, PassInputs);
|
||||
}
|
||||
```
|
||||
|
||||
### 超采样之后的UV转换
|
||||
`Tonemap`之后的Pass因为超采样的关系使得ViewportResoution与BufferResoution不同,所以需要使用`SHADER_PARAMETER(FScreenTransform, SvPositionToInputTextureUV)`以及`FScreenTransform::ChangeTextureBasisFromTo`计算变换比例。具体可以参考FFXAAPS。
|
||||
|
||||
SvPosition => ViewportUV => TextureUV:
|
||||
```c++
|
||||
PassParameters->SvPositionToInputTextureUV = (
|
||||
FScreenTransform::SvPositionToViewportUV(Output.ViewRect) *
|
||||
FScreenTransform::ChangeTextureBasisFromTo(FScreenPassTextureViewport(Inputs.SceneColorSlice), FScreenTransform::ETextureBasis::ViewportUV, FScreenTransform::ETextureBasis::TextureUV));
|
||||
```
|
||||
|
||||
之后在Shader中:
|
||||
```c++
|
||||
Texture2D SceneColorTexture;
|
||||
SamplerState SceneColorSampler;
|
||||
FScreenTransform SvPositionToInputTextureUV;
|
||||
|
||||
void MainPS(
|
||||
float4 SvPosition : SV_POSITION,
|
||||
out float4 OutColor : SV_Target0)
|
||||
{
|
||||
float2 SceneColorUV = ApplyScreenTransform(SvPosition.xy, SvPositionToInputTextureUV);
|
||||
OutColor.rgba = SceneColorTexture.SampleLevel(SceneColorSampler, UV, 0).rgba;
|
||||
}
|
||||
```
|
||||
|
||||
## 在else代码段
|
||||
在else代码段中添加新Pass禁用代码段。不添加则会在切换到其他View(比如材质编辑器的Preview)时崩溃。例如:
|
||||
```c++
|
||||
if(bPostProcessingEnabled){
|
||||
...
|
||||
...
|
||||
}else
|
||||
{
|
||||
PassSequence.SetEnabled(EPass::XXX, false);
|
||||
|
||||
PassSequence.SetEnabled(EPass::MotionBlur, false);
|
||||
PassSequence.SetEnabled(EPass::Tonemap, true);
|
||||
PassSequence.SetEnabled(EPass::FXAA, false);
|
||||
PassSequence.SetEnabled(EPass::PostProcessMaterialAfterTonemapping, false);
|
||||
PassSequence.SetEnabled(EPass::VisualizeDepthOfField, false);
|
||||
PassSequence.SetEnabled(EPass::VisualizeLocalExposure, false);
|
||||
PassSequence.Finalize();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
##
|
||||
PS:
|
||||
1. `Tonemap`之后的Pass因为超采样的关系使得ViewportResoution与BufferResoution不同,所以需要使用`SHADER_PARAMETER(FScreenTransform, SvPositionToInputTextureUV)`以及`FScreenTransform::ChangeTextureBasisFromTo`计算变换比例。具体可以参考FFXAAPS。
|
||||
1. ToneMapPass 传入FScreenPassTextureSlice,传出FScreenPassTexture。如果关闭Tonemap则直接运行`SceneColor = FScreenPassTexture(SceneColorSlice);`。
|
||||
2. `MotionBlur`~`Tonemap`
|
||||
3. `MotionBlur`之前(后处理材质BL_BeforeTonemapping之前),传入FScreenPassTexture SceneColor进行绘制。
|
||||
##
|
||||
```c++
|
||||
FPostProcessMaterialInputs PassInputs;
|
||||
PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, PassInputs.OverrideOutput);
|
||||
PassInputs.SetInput(EPostProcessMaterialInput::SceneColor, FScreenPassTexture::CopyFromSlice(GraphBuilder, SceneColorSlice));
|
||||
```
|
||||
|
||||
创建`FScreenPassTextureSlice`:
|
||||
```c++
|
||||
FScreenPassTextureSlice SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor);//FScreenPassTexture SceneColor
|
||||
```
|
||||
|
||||
将渲染结果转换成`FScreenPassTextureSlice`:
|
||||
```c++
|
||||
SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddToonPostProcessBeforeTonemappingPass(GraphBuilder, View, PassInputs));
|
||||
```
|
||||
|
||||
```c++
|
||||
// Allows for the scene color to be the slice of an array between temporal upscaler and tonemaper.
|
||||
FScreenPassTextureSlice SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, SceneColor);
|
||||
```
|
||||
## Viewport => TextureUV
|
||||
```c++
|
||||
PassParameters->SvPositionToInputTextureUV = (
|
||||
FScreenTransform::SvPositionToViewportUV(Output.ViewRect) *
|
||||
FScreenTransform::ChangeTextureBasisFromTo(FScreenPassTextureViewport(Inputs.SceneColorSlice), FScreenTransform::ETextureBasis::ViewportUV, FScreenTransform::ETextureBasis::TextureUV));
|
||||
```
|
||||
|
||||
在Shader中:
|
||||
```c++
|
||||
Texture2D SceneColorTexture;
|
||||
SamplerState SceneColorSampler;
|
||||
FScreenTransform SvPositionToInputTextureUV;
|
||||
|
||||
void MainPS(
|
||||
float4 SvPosition : SV_POSITION,
|
||||
out float4 OutColor : SV_Target0)
|
||||
{
|
||||
float2 SceneColorUV = ApplyScreenTransform(SvPosition.xy, SvPositionToInputTextureUV);
|
||||
OutColor.rgba = SceneColorTexture.SampleLevel(SceneColorSampler, UV, 0).rgba;
|
||||
}
|
||||
```
|
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Toon ToneMapping
|
||||
date: 2025-01-19 21:02:19
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
378
03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/BasePass C++.md
Normal file
378
03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/BasePass C++.md
Normal file
@@ -0,0 +1,378 @@
|
||||
---
|
||||
title: Untitled
|
||||
date: 2024-09-26 18:41:24
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# RenderBasePass()
|
||||
传入RenderBasePass()的DepthStencil逻辑如下:
|
||||
```c++
|
||||
const FExclusiveDepthStencil::Type BasePassDepthStencilAccess =
|
||||
bAllowReadOnlyDepthBasePass
|
||||
? FExclusiveDepthStencil::DepthRead_StencilWrite
|
||||
: FExclusiveDepthStencil::DepthWrite_StencilWrite;
|
||||
```
|
||||
|
||||
|
||||
FDeferredShadingSceneRenderer::RenderBasePass() =>
|
||||
FDeferredShadingSceneRenderer::RenderBasePassInternal() =>
|
||||
|
||||
|
||||
FBasePassMeshProcessor::TryAddMeshBatch =>
|
||||
|
||||
## 大致流程
|
||||
1. 创建MRT并绑定、取得深度缓存。
|
||||
```c++
|
||||
const FExclusiveDepthStencil ExclusiveDepthStencil(BasePassDepthStencilAccess);
|
||||
|
||||
TStaticArray<FTextureRenderTargetBinding, MaxSimultaneousRenderTargets> BasePassTextures;
|
||||
uint32 BasePassTextureCount = SceneTextures.GetGBufferRenderTargets(BasePassTextures);
|
||||
Strata::AppendStrataMRTs(*this, BasePassTextureCount, BasePassTextures);
|
||||
TArrayView<FTextureRenderTargetBinding> BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);
|
||||
FRDGTextureRef BasePassDepthTexture = SceneTextures.Depth.Target;
|
||||
```
|
||||
2. GBuffer Clear
|
||||
```c++
|
||||
GraphBuilder.AddPass(RDG_EVENT_NAME("GBufferClear"), PassParameters, ERDGPassFlags::Raster,
|
||||
[PassParameters, ColorLoadAction, SceneColorClearValue](FRHICommandList& RHICmdList)
|
||||
{
|
||||
// If no fast-clear action was used, we need to do an MRT shader clear.
|
||||
if (ColorLoadAction == ERenderTargetLoadAction::ENoAction)
|
||||
{
|
||||
const FRenderTargetBindingSlots& RenderTargets = PassParameters->RenderTargets;
|
||||
FLinearColor ClearColors[MaxSimultaneousRenderTargets];
|
||||
FRHITexture* Textures[MaxSimultaneousRenderTargets];
|
||||
int32 TextureIndex = 0;
|
||||
|
||||
RenderTargets.Enumerate([&](const FRenderTargetBinding& RenderTarget)
|
||||
{
|
||||
FRHITexture* TextureRHI = RenderTarget.GetTexture()->GetRHI();
|
||||
ClearColors[TextureIndex] = TextureIndex == 0 ? SceneColorClearValue : TextureRHI->GetClearColor();
|
||||
Textures[TextureIndex] = TextureRHI;
|
||||
++TextureIndex;
|
||||
});
|
||||
|
||||
// Clear color only; depth-stencil is fast cleared.
|
||||
DrawClearQuadMRT(RHICmdList, true, TextureIndex, ClearColors, false, 0, false, 0);
|
||||
}
|
||||
});
|
||||
```
|
||||
3. RenderTargetBindingSlots
|
||||
```c++
|
||||
// Render targets bindings should remain constant at this point.
|
||||
FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView);
|
||||
BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);
|
||||
```
|
||||
4. RenderBasePassInternal()
|
||||
5. RenderAnisotropyPass()
|
||||
|
||||
# MeshDraw
|
||||
## RenderBasePassInternal()
|
||||
RenderNaniteBasePass()为一个Lambda,最终调用**Nanite::DrawBasePass()** 渲染Nanite物体的BasePass。其他相关渲染代码如下:
|
||||
```c++
|
||||
SCOPE_CYCLE_COUNTER(STAT_BasePassDrawTime);
|
||||
RDG_EVENT_SCOPE(GraphBuilder, "BasePass");
|
||||
RDG_GPU_STAT_SCOPE(GraphBuilder, Basepass);
|
||||
|
||||
const bool bDrawSceneViewsInOneNanitePass = Views.Num() > 1 && Nanite::ShouldDrawSceneViewsInOneNanitePass(Views[0]);
|
||||
if (bParallelBasePass)//并行渲染模式
|
||||
{
|
||||
RDG_WAIT_FOR_TASKS_CONDITIONAL(GraphBuilder, IsBasePassWaitForTasksEnabled());
|
||||
|
||||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
||||
{
|
||||
FViewInfo& View = Views[ViewIndex];
|
||||
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
||||
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
||||
View.BeginRenderView();
|
||||
|
||||
const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen;
|
||||
|
||||
FMeshPassProcessorRenderState DrawRenderState;
|
||||
SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState);
|
||||
|
||||
FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
|
||||
PassParameters->View = View.GetShaderParameters();
|
||||
PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
||||
PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, bLumenGIEnabled);
|
||||
PassParameters->RenderTargets = BasePassRenderTargets;
|
||||
PassParameters->RenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, View, FVariableRateShadingImageManager::EVRSPassType::BasePass);
|
||||
|
||||
const bool bShouldRenderView = View.ShouldRenderView();
|
||||
if (bShouldRenderView)
|
||||
{
|
||||
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
||||
|
||||
GraphBuilder.AddPass(
|
||||
RDG_EVENT_NAME("BasePassParallel"),
|
||||
PassParameters,
|
||||
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
||||
[this, &View, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
||||
{
|
||||
FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), View, FParallelCommandListBindings(PassParameters));
|
||||
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||||
});
|
||||
}
|
||||
|
||||
const bool bShouldRenderViewForNanite = bNaniteEnabled && (!bDrawSceneViewsInOneNanitePass || ViewIndex == 0); // when bDrawSceneViewsInOneNanitePass, the first view should cover all the other atlased ones
|
||||
if (bShouldRenderViewForNanite)
|
||||
{
|
||||
// Should always have a full Z prepass with Nanite
|
||||
check(ShouldRenderPrePass());
|
||||
//渲染Nanite物体BasePass
|
||||
RenderNaniteBasePass(View, ViewIndex);
|
||||
}
|
||||
|
||||
//渲染编辑器相关的图元物体
|
||||
RenderEditorPrimitives(GraphBuilder, PassParameters, View, DrawRenderState, InstanceCullingManager);
|
||||
|
||||
//渲染大气
|
||||
if (bShouldRenderView && View.Family->EngineShowFlags.Atmosphere)
|
||||
{
|
||||
FOpaqueBasePassParameters* SkyPassPassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
|
||||
SkyPassPassParameters->BasePass = PassParameters->BasePass;
|
||||
SkyPassPassParameters->RenderTargets = BasePassRenderTargets;
|
||||
SkyPassPassParameters->View = View.GetShaderParameters();
|
||||
SkyPassPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
||||
|
||||
View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassPassParameters->InstanceCullingDrawParams);
|
||||
|
||||
GraphBuilder.AddPass(
|
||||
RDG_EVENT_NAME("SkyPassParallel"),
|
||||
SkyPassPassParameters,
|
||||
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
||||
[this, &View, SkyPassPassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
||||
{
|
||||
FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), View, FParallelCommandListBindings(SkyPassPassParameters));
|
||||
View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &SkyPassPassParameters->InstanceCullingDrawParams);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
||||
{
|
||||
FViewInfo& View = Views[ViewIndex];
|
||||
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
||||
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
||||
View.BeginRenderView();
|
||||
|
||||
const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen;
|
||||
|
||||
FMeshPassProcessorRenderState DrawRenderState;
|
||||
SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState);
|
||||
|
||||
FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
|
||||
PassParameters->View = View.GetShaderParameters();
|
||||
PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
||||
PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, bLumenGIEnabled);
|
||||
PassParameters->RenderTargets = BasePassRenderTargets;
|
||||
PassParameters->RenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, View, FVariableRateShadingImageManager::EVRSPassType::BasePass);
|
||||
|
||||
const bool bShouldRenderView = View.ShouldRenderView();
|
||||
if (bShouldRenderView)
|
||||
{
|
||||
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
||||
|
||||
GraphBuilder.AddPass(
|
||||
RDG_EVENT_NAME("BasePass"),
|
||||
PassParameters,
|
||||
ERDGPassFlags::Raster,
|
||||
[this, &View, PassParameters](FRHICommandList& RHICmdList)
|
||||
{
|
||||
SetStereoViewport(RHICmdList, View, 1.0f);
|
||||
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const bool bShouldRenderViewForNanite = bNaniteEnabled && (!bDrawSceneViewsInOneNanitePass || ViewIndex == 0); // when bDrawSceneViewsInOneNanitePass, the first view should cover all the other atlased ones
|
||||
if (bShouldRenderViewForNanite)
|
||||
{
|
||||
// Should always have a full Z prepass with Nanite
|
||||
check(ShouldRenderPrePass());
|
||||
|
||||
RenderNaniteBasePass(View, ViewIndex);
|
||||
}
|
||||
|
||||
RenderEditorPrimitives(GraphBuilder, PassParameters, View, DrawRenderState, InstanceCullingManager);
|
||||
|
||||
if (bShouldRenderView && View.Family->EngineShowFlags.Atmosphere)
|
||||
{
|
||||
FOpaqueBasePassParameters* SkyPassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
|
||||
SkyPassParameters->BasePass = PassParameters->BasePass;
|
||||
SkyPassParameters->RenderTargets = BasePassRenderTargets;
|
||||
SkyPassParameters->View = View.GetShaderParameters();
|
||||
SkyPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
||||
|
||||
View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassParameters->InstanceCullingDrawParams);
|
||||
|
||||
GraphBuilder.AddPass(
|
||||
RDG_EVENT_NAME("SkyPass"),
|
||||
SkyPassParameters,
|
||||
ERDGPassFlags::Raster,
|
||||
[this, &View, SkyPassParameters](FRHICommandList& RHICmdList)
|
||||
{
|
||||
SetStereoViewport(RHICmdList, View, 1.0f);
|
||||
View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].DispatchDraw(nullptr, RHICmdList, &SkyPassParameters->InstanceCullingDrawParams);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### SetDepthStencilStateForBasePass()
|
||||
```c++
|
||||
void SetDepthStencilStateForBasePass(
|
||||
FMeshPassProcessorRenderState& DrawRenderState,
|
||||
ERHIFeatureLevel::Type FeatureLevel,
|
||||
bool bDitheredLODTransition,
|
||||
const FMaterial& MaterialResource,
|
||||
bool bEnableReceiveDecalOutput,
|
||||
bool bForceEnableStencilDitherState)
|
||||
{
|
||||
const bool bMaskedInEarlyPass = (MaterialResource.IsMasked() || bDitheredLODTransition) && MaskedInEarlyPass(GShaderPlatformForFeatureLevel[FeatureLevel]);
|
||||
if (bEnableReceiveDecalOutput)
|
||||
{
|
||||
if (bMaskedInEarlyPass)
|
||||
{
|
||||
SetDepthStencilStateForBasePass_Internal<false, CF_Equal>(DrawRenderState, FeatureLevel);
|
||||
}
|
||||
else if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite)
|
||||
{
|
||||
SetDepthStencilStateForBasePass_Internal<true, CF_GreaterEqual>(DrawRenderState, FeatureLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDepthStencilStateForBasePass_Internal<false, CF_GreaterEqual>(DrawRenderState, FeatureLevel);
|
||||
}
|
||||
}
|
||||
else if (bMaskedInEarlyPass)
|
||||
{
|
||||
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Equal>::GetRHI());
|
||||
}
|
||||
|
||||
if (bForceEnableStencilDitherState)
|
||||
{
|
||||
SetDepthStencilStateForBasePass_Internal<false, CF_Equal>(DrawRenderState, FeatureLevel);
|
||||
}
|
||||
}
|
||||
```
|
||||
## AnisotropyPass
|
||||
Anisotropy的RT设置:
|
||||
- RenderTarget:SceneTextures.GBufferF。
|
||||
- DepthStencil:SceneTextures.Depth.Target。**ERenderTargetLoadAction::ELoad**、**FExclusiveDepthStencil::DepthRead_StencilNop**
|
||||
|
||||
### 管线状态
|
||||
在FAnisotropyMeshProcessor::CollectPSOInitializers()中:
|
||||
```c++
|
||||
ETextureCreateFlags GBufferFCreateFlags;
|
||||
EPixelFormat GBufferFPixelFormat = FSceneTextures::GetGBufferFFormatAndCreateFlags(GBufferFCreateFlags);
|
||||
AddRenderTargetInfo(GBufferFPixelFormat, GBufferFCreateFlags, RenderTargetsInfo);
|
||||
SetupDepthStencilInfo(PF_DepthStencil, SceneTexturesConfig.DepthCreateFlags, ERenderTargetLoadAction::ELoad,
|
||||
ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilNop, RenderTargetsInfo);
|
||||
```
|
||||
|
||||
```c++
|
||||
void SetupDepthStencilInfo(
|
||||
EPixelFormat DepthStencilFormat,
|
||||
ETextureCreateFlags DepthStencilCreateFlags,
|
||||
ERenderTargetLoadAction DepthTargetLoadAction,
|
||||
ERenderTargetLoadAction StencilTargetLoadAction,
|
||||
FExclusiveDepthStencil DepthStencilAccess,
|
||||
FGraphicsPipelineRenderTargetsInfo& RenderTargetsInfo)
|
||||
{
|
||||
// Setup depth stencil state
|
||||
RenderTargetsInfo.DepthStencilTargetFormat = DepthStencilFormat;
|
||||
RenderTargetsInfo.DepthStencilTargetFlag = DepthStencilCreateFlags;
|
||||
|
||||
RenderTargetsInfo.DepthTargetLoadAction = DepthTargetLoadAction;
|
||||
RenderTargetsInfo.StencilTargetLoadAction = StencilTargetLoadAction;
|
||||
RenderTargetsInfo.DepthStencilAccess = DepthStencilAccess;
|
||||
|
||||
const ERenderTargetStoreAction StoreAction = EnumHasAnyFlags(RenderTargetsInfo.DepthStencilTargetFlag, TexCreate_Memoryless) ? ERenderTargetStoreAction::ENoAction : ERenderTargetStoreAction::EStore;
|
||||
RenderTargetsInfo.DepthTargetStoreAction = RenderTargetsInfo.DepthStencilAccess.IsUsingDepth() ? StoreAction : ERenderTargetStoreAction::ENoAction;
|
||||
RenderTargetsInfo.StencilTargetStoreAction = RenderTargetsInfo.DepthStencilAccess.IsUsingStencil() ? StoreAction : ERenderTargetStoreAction::ENoAction;
|
||||
}
|
||||
```
|
||||
|
||||
### ParallelRendering
|
||||
AnisotropyPass支持并行渲染,并行渲染的判断逻辑为
|
||||
```c++
|
||||
const bool bEnableParallelBasePasses = GRHICommandList.UseParallelAlgorithms() && CVarParallelBasePass.GetValueOnRenderThread();
|
||||
```
|
||||
看得出判断条件是:
|
||||
1. 显卡是否支持并行渲染。
|
||||
2. CVar(r.ParallelBasePass)是否开启并行渲染。
|
||||
|
||||
从AnisotropyPass可以看得出并行渲染与一般渲染的差别在于:
|
||||
1. FRenderTargetBinding绑定时的ERenderTargetLoadAction不同,**并行为ELoad**;**普通渲染为EClear**。
|
||||
2. 调用AddPass添加了**ERDGPassFlags::SkipRenderPass**标记。
|
||||
3. 并行渲染会在AddPass中构建**FRDGParallelCommandListSet ParallelCommandListSet**,并作为传入**DispatchDraw**;普通渲染传递nullptr。
|
||||
4. 普通渲染会额外调用**SetStereoViewport(RHICmdList, View);**,本质是调用RHICmdList.SetViewport来设置View。
|
||||
|
||||
### Code
|
||||
```c++
|
||||
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderAnisotropyPass);
|
||||
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderAnisotropyPass, FColor::Emerald);
|
||||
SCOPE_CYCLE_COUNTER(STAT_AnisotropyPassDrawTime);
|
||||
RDG_GPU_STAT_SCOPE(GraphBuilder, RenderAnisotropyPass);
|
||||
|
||||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||||
{
|
||||
FViewInfo& View = Views[ViewIndex];
|
||||
|
||||
if (View.ShouldRenderView())
|
||||
{
|
||||
FParallelMeshDrawCommandPass& ParallelMeshPass = View.ParallelMeshDrawCommandPasses[EMeshPass::AnisotropyPass];
|
||||
|
||||
if (!ParallelMeshPass.HasAnyDraw())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
View.BeginRenderView();
|
||||
|
||||
auto* PassParameters = GraphBuilder.AllocParameters<FAnisotropyPassParameters>();
|
||||
PassParameters->View = View.GetShaderParameters();
|
||||
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilNop);
|
||||
|
||||
ParallelMeshPass.BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
||||
if (bDoParallelPass)
|
||||
{
|
||||
AddClearRenderTargetPass(GraphBuilder, SceneTextures.GBufferF);
|
||||
|
||||
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.GBufferF, ERenderTargetLoadAction::ELoad);
|
||||
|
||||
GraphBuilder.AddPass(
|
||||
RDG_EVENT_NAME("AnisotropyPassParallel"),
|
||||
PassParameters,
|
||||
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
||||
[this, &View, &ParallelMeshPass, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
||||
{
|
||||
FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_AnisotropyPass), View, FParallelCommandListBindings(PassParameters));
|
||||
|
||||
ParallelMeshPass.DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.GBufferF, ERenderTargetLoadAction::EClear);
|
||||
|
||||
GraphBuilder.AddPass(
|
||||
RDG_EVENT_NAME("AnisotropyPass"),
|
||||
PassParameters,
|
||||
ERDGPassFlags::Raster,
|
||||
[this, &View, &ParallelMeshPass, PassParameters](FRHICommandList& RHICmdList)
|
||||
{
|
||||
SetStereoViewport(RHICmdList, View);
|
||||
|
||||
ParallelMeshPass.DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,659 @@
|
||||
---
|
||||
title: GBuffer&Material&BasePass
|
||||
date: 2023-12-08 17:34:58
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
# # GBuffer
|
||||
目前UE5.3会调用
|
||||
- WriteGBufferInfoAutogen()
|
||||
- **EncodeGBufferToMRT()**
|
||||
动态生成BasePassPixelShader.usf中的**EncodeGBufferToMRT()** 的代码,并且会生成一个AutogenShaderHeaders.ush文件。其路径为:
|
||||
`Engine\Intermediate\ShaderAutogen\PCD3D_SM5`或者`Engine\Intermediate\ShaderAutogen\PCD3D_ES3_1`
|
||||
|
||||
1. ***给FGBufferData添加结构体数据时需要在此添加额外代码逻辑***
|
||||
2. GBuffer精度在FetchLegacyGBufferInfo()设置。
|
||||
3. 是否往GBuffer中写入Velocity,主要靠这个宏**WRITES_VELOCITY_TO_GBUFFER**。具体决定其数值的逻辑位于**FShaderGlobalDefines FetchShaderGlobalDefines**。主要还是靠**r.VelocityOutputPass**进行开启。
|
||||
1. PS. MSAA以及VR绝对不会开启Velocity输出选项。还有就是**r.Velocity.ForceOutput**,但经过测试不开启r.VelocityOutputPass依然无法输出。以及FPrimitiveSceneProxy的bAlwaysHasVelocity与bHasWorldPositionOffsetVelocity。
|
||||
2. 其他相关FSR、TSR?
|
||||
4. 如何添加GBuffer
|
||||
1. https://zhuanlan.zhihu.com/p/568775542
|
||||
2. https://zhuanlan.zhihu.com/p/677772284
|
||||
|
||||
## UE5 GBuffer内容:
|
||||
[[UE GBuffer存储数据]]
|
||||
```c#
|
||||
OutGBufferA(MRT1) = WorldNormal/PerObjectGBufferData (GBT_Float_16_16_16_16/GBT_Unorm_11_11_10/GBT_Unorm_8_8_8_8)
|
||||
OutGBufferB(MRT2) = Metallic/Specular/Roughness/EncodeShadingModelIdAndSelectiveOutputMask (GBT_Float_16_16_16_16/GBT_Unorm_8_8_8_8)
|
||||
OutGBufferC(MRT3) = BaseColor/GBufferAO (GBT_Unorm_8_8_8_8)
|
||||
OutGBufferD = GBuffer.CustomData (GBT_Unorm_8_8_8_8)
|
||||
OutGBufferE = GBuffer.PrecomputedShadowFactors (GBT_Unorm_8_8_8_8)
|
||||
TargetVelocity / OutGBufferF = velocity / tangent (默认不开启 带有深度<开启Lumen与距离场 或者 开启光线追踪> GBC_Raw_Float_16_16_16_16 不带深度 GBC_Raw_Float_16_16)
|
||||
TargetSeparatedMainDirLight = SingleLayerWater相关 (有SingleLayerWater才会开启 GBC_Raw_Float_11_11_10)
|
||||
OutGBufferF = Anisotropy
|
||||
|
||||
// 0..1, 2 bits, use CastContactShadow(GBuffer) or HasDynamicIndirectShadowCasterRepresentation(GBuffer) to extract
|
||||
half PerObjectGBufferData;
|
||||
```
|
||||
GBuffer相关信息(精度、顺序)可以参考FetchLegacyGBufferInfo()。
|
||||
- 不存在Velocity与Tangent:
|
||||
- OutGBufferD(MRT4)
|
||||
- OutGBufferD(MRT5)
|
||||
- TargetSeparatedMainDirLight(MRT6)
|
||||
- 存在Velocity:
|
||||
- TargetVelocity(MRT4)
|
||||
- OutGBufferD(MRT5)
|
||||
- OutGBufferE(MRT6)
|
||||
- TargetSeparatedMainDirLight(MRT7)
|
||||
- 存在Tangent:
|
||||
- OutGBufferF(MRT4)
|
||||
- OutGBufferD(MRT5)
|
||||
- OutGBufferE(MRT6)
|
||||
- TargetSeparatedMainDirLight(MRT7)
|
||||
|
||||
几个动态MRT的存在条件与Shader判断宏:
|
||||
- OutGBufferE(PrecomputedShadowFactors):r.AllowStaticLighting = 1
|
||||
- GBUFFER_HAS_PRECSHADOWFACTOR
|
||||
- WRITES_PRECSHADOWFACTOR_ZERO
|
||||
- WRITES_PRECSHADOWFACTOR_TO_GBUFFER
|
||||
- TargetVelocity:(IsUsingBasePassVelocity(Platform) || Layout == GBL_ForceVelocity) ? 1 : 0;//r.VelocityOutputPass = 1
|
||||
- r.VelocityOutputPass = 1时,会对骨骼物体以及WPO材质物体输出速度。因为大概率会使用距离场阴影以及VSM,所以会占用GBuffer Velocity所有通道。
|
||||
- GBUFFER_HAS_VELOCITY
|
||||
- WRITES_VELOCITY_TO_GBUFFER
|
||||
- SingleLayerWater
|
||||
- 默认不会写入GBuffer需要符合以下条件:const bool bNeedsSeparateMainDirLightTexture = IsWaterDistanceFieldShadowEnabled(Parameters.Platform) || IsWaterVirtualShadowMapFilteringEnabled(Parameters.Platform);
|
||||
- r.Water.SingleLayer.ShadersSupportDistanceFieldShadow = 1
|
||||
- r.Water.SingleLayer.ShadersSupportVSMFiltering = 1
|
||||
- const bool bIsSingleLayerWater = Parameters.MaterialParameters.ShadingModels.HasShadingModel(MSM_SingleLayerWater);
|
||||
- Tangent:false,目前单独使用另一组MRT来存储。
|
||||
- ~~GBUFFER_HAS_TANGENT~`
|
||||
|
||||
### ToonGBuffer修改&数据存储
|
||||
```c#
|
||||
OutGBufferA:PerObjectGBufferData => 可以存储额外的有关Tonn渲染功能参数。
|
||||
OutGBufferB:Metallic/Specular/Roughness =>
|
||||
? / SpcularPower(控制高光亮度与Mask) / ? / ?
|
||||
//ToonHairMask OffsetShadowMask/SpcularMask/SpecularValue
|
||||
OutGBufferC:GBufferAO =>
|
||||
ToonAO
|
||||
OutGBufferD:CustomData.xyzw =>
|
||||
ShadowColor.rgb / NoLOffset //ShadowColor这里可以在Material里通过主光向量、ShadowStep、Shadow羽化计算多层阴影效果。
|
||||
OutGBufferE:GBuffer.PrecomputedShadowFactors.xyzw =>
|
||||
ToonDataID/ ToonOutlineDataID / OutlineMask(控制Outline绘制以及Outline强度) / ToonObjectID(判断是否是一个物体)
|
||||
TargetVelocity / OutGBufferF = velocity / tangent //目前先不考虑输出Velocity的情况
|
||||
? / ? / ? / ?
|
||||
```
|
||||
|
||||
ToonDataID在材质编辑器中会存在SubsurfaceColor.a中,ToonOutlineDataID在材质编辑器中会存在CustomData1(引脚名为ToonBufferB,考虑到Subsurface有一个CurvatureMap需要使用CustomData0,所以这里使用了CustomData1)。
|
||||
|
||||
|
||||
蓝色协议的方案
|
||||
![[蓝色协议的方案#GBuffer]]
|
||||
|
||||
***额外添加相关宏(逻辑位于ShaderCompiler.cpp)***
|
||||
- **GBUFFER_HAS_TOONDATA**
|
||||
|
||||
### 修改GBuffer格式
|
||||
- [[#ShaderMaterialDerivedHelpers.cpp中的CalculateDerivedMaterialParameters()]]控制在BasePassPixelShader.usf中的MRT宏是否为true。
|
||||
- [[#BasePassRendering.cpp中ModifyBasePassCSPSCompilationEnvironment()]]控制Velocity与SingleLayerWater相关的RT精度。
|
||||
- [[#GBufferInfo.cpp中的FetchLegacyGBufferInfo()]]控制GBuffer精度以及数据打包情况。
|
||||
|
||||
#### BasePassRendering.cpp中ModifyBasePassCSPSCompilationEnvironment()
|
||||
```c++
|
||||
void ModifyBasePassCSPSCompilationEnvironment()
|
||||
{
|
||||
...
|
||||
const bool bOutputVelocity = (GBufferLayout == GBL_ForceVelocity) ||
|
||||
FVelocityRendering::BasePassCanOutputVelocity(Parameters.Platform);
|
||||
if (bOutputVelocity)
|
||||
{
|
||||
// As defined in BasePassPixelShader.usf. Also account for Strata setting velocity in slot 1 as described in FetchLegacyGBufferInfo.
|
||||
const int32 VelocityIndex = Strata::IsStrataEnabled() ? 1 : (IsForwardShadingEnabled(Parameters.Platform) ? 1 : 4);
|
||||
OutEnvironment.SetRenderTargetOutputFormat(VelocityIndex, PF_G16R16);
|
||||
}
|
||||
...
|
||||
const bool bNeedsSeparateMainDirLightTexture = IsWaterDistanceFieldShadowEnabled(Parameters.Platform) || IsWaterVirtualShadowMapFilteringEnabled(Parameters.Platform);
|
||||
if (bIsSingleLayerWater && bNeedsSeparateMainDirLightTexture)
|
||||
{
|
||||
// See FShaderCompileUtilities::FetchGBufferParamsRuntime for the details
|
||||
const bool bHasTangent = false;
|
||||
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
|
||||
bool bHasPrecShadowFactor = (CVar ? (CVar->GetValueOnAnyThread() != 0) : 1);
|
||||
|
||||
uint32 TargetSeparatedMainDirLight = 5;
|
||||
if (bOutputVelocity == false && bHasTangent == false)
|
||||
{
|
||||
TargetSeparatedMainDirLight = 5;
|
||||
if (bHasPrecShadowFactor)
|
||||
{
|
||||
TargetSeparatedMainDirLight = 6;
|
||||
}
|
||||
}
|
||||
else if (bOutputVelocity)
|
||||
{
|
||||
TargetSeparatedMainDirLight = 6;
|
||||
if (bHasPrecShadowFactor)
|
||||
{
|
||||
TargetSeparatedMainDirLight = 7;
|
||||
}
|
||||
}
|
||||
else if (bHasTangent)
|
||||
{
|
||||
TargetSeparatedMainDirLight = 6;
|
||||
if (bHasPrecShadowFactor)
|
||||
{
|
||||
TargetSeparatedMainDirLight = 7;
|
||||
}
|
||||
}
|
||||
OutEnvironment.SetRenderTargetOutputFormat(TargetSeparatedMainDirLight, PF_FloatR11G11B10);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### GBufferInfo.cpp中的FetchLegacyGBufferInfo()
|
||||
控制GBuffer精度以及数据打包情况。
|
||||
|
||||
#### ShaderMaterialDerivedHelpers.cpp中的CalculateDerivedMaterialParameters()
|
||||
```c++
|
||||
else if (Mat.IS_BASE_PASS)
|
||||
{
|
||||
Dst.PIXELSHADEROUTPUT_BASEPASS = 1;
|
||||
if (Dst.USES_GBUFFER)
|
||||
{
|
||||
Dst.PIXELSHADEROUTPUT_MRT0 = (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || Dst.NEEDS_BASEPASS_VERTEX_FOGGING || Mat.USES_EMISSIVE_COLOR || SrcGlobal.ALLOW_STATIC_LIGHTING || Mat.MATERIAL_SHADINGMODEL_SINGLELAYERWATER);
|
||||
Dst.PIXELSHADEROUTPUT_MRT1 = ((!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || !Mat.MATERIAL_SHADINGMODEL_UNLIT));
|
||||
Dst.PIXELSHADEROUTPUT_MRT2 = ((!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || !Mat.MATERIAL_SHADINGMODEL_UNLIT));
|
||||
Dst.PIXELSHADEROUTPUT_MRT3 = ((!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || !Mat.MATERIAL_SHADINGMODEL_UNLIT));
|
||||
if (SrcGlobal.GBUFFER_HAS_VELOCITY || SrcGlobal.GBUFFER_HAS_TANGENT)
|
||||
{
|
||||
Dst.PIXELSHADEROUTPUT_MRT4 = Dst.WRITES_VELOCITY_TO_GBUFFER || SrcGlobal.GBUFFER_HAS_TANGENT;
|
||||
Dst.PIXELSHADEROUTPUT_MRT5 = (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || Dst.WRITES_CUSTOMDATA_TO_GBUFFER);
|
||||
Dst.PIXELSHADEROUTPUT_MRT6 = (Dst.GBUFFER_HAS_PRECSHADOWFACTOR && (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || (Dst.WRITES_PRECSHADOWFACTOR_TO_GBUFFER && !Mat.MATERIAL_SHADINGMODEL_UNLIT)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Dst.PIXELSHADEROUTPUT_MRT4 = (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || Dst.WRITES_CUSTOMDATA_TO_GBUFFER);
|
||||
Dst.PIXELSHADEROUTPUT_MRT5 = (Dst.GBUFFER_HAS_PRECSHADOWFACTOR && (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || (Dst.WRITES_PRECSHADOWFACTOR_TO_GBUFFER && !Mat.MATERIAL_SHADINGMODEL_UNLIT)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Dst.PIXELSHADEROUTPUT_MRT0 = true;
|
||||
// we also need MRT for thin translucency due to dual blending if we are not on the fallback path
|
||||
Dst.PIXELSHADEROUTPUT_MRT1 = (Dst.WRITES_VELOCITY_TO_GBUFFER || (Mat.DUAL_SOURCE_COLOR_BLENDING_ENABLED && Dst.MATERIAL_WORKS_WITH_DUAL_SOURCE_COLOR_BLENDING));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
位于FShaderCompileUtilities::ApplyDerivedDefines(),新版本逻辑遍历数据由GBufferInfo.cpp中的FetchLegacyGBufferInfo()处理。
|
||||
```c++
|
||||
#if 1
|
||||
static bool bTestNewVersion = true;
|
||||
if (bTestNewVersion)
|
||||
{
|
||||
//if (DerivedDefines.USES_GBUFFER)
|
||||
{
|
||||
for (int32 Iter = 0; Iter < FGBufferInfo::MaxTargets; Iter++)
|
||||
{
|
||||
if (bTargetUsage[Iter])
|
||||
{
|
||||
FString TargetName = FString::Printf(TEXT("PIXELSHADEROUTPUT_MRT%d"), Iter);
|
||||
OutEnvironment.SetDefine(TargetName.GetCharArray().GetData(), TEXT("1"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This uses the legacy logic from CalculateDerivedMaterialParameters(); Just keeping it around momentarily for testing during the transition.
|
||||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT0)
|
||||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT1)
|
||||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT2)
|
||||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT3)
|
||||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT4)
|
||||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT5)
|
||||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT6)
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
### MaterialTemplate.ush
|
||||
MaterialTemplate.ush中定义许多模版函数,里面的具体内容会在HLSLMaterialTranslator.h中的**GetMaterialShaderCode()** 中添加。最后这些函数会在BassPassPixelShader.usf中调用。
|
||||
|
||||
bool bEnableExecutionFlow的作用为是否使用新的材质HLSL生成器,默认为0。
|
||||
```c++
|
||||
static TAutoConsoleVariable<int32> CVarMaterialEnableNewHLSLGenerator(
|
||||
TEXT("r.MaterialEnableNewHLSLGenerator"),
|
||||
0,
|
||||
TEXT("Enables the new (WIP) material HLSL generator.\n")
|
||||
TEXT("0 - Don't allow\n")
|
||||
TEXT("1 - Allow if enabled by material\n")
|
||||
TEXT("2 - Force all materials to use new generator\n"),
|
||||
ECVF_RenderThreadSafe | ECVF_ReadOnly);
|
||||
```
|
||||
这个和新版材质HLSL生成器有关,相关生成代码为**MaterialEmitHLSL()**=>调用**GenerateMaterialTemplateHLSL()**
|
||||
|
||||
bCompileForComputeShader = Material->IsLightFunction();
|
||||
GetPerInstanceCustomDataX分为Vertex与Pixel版本。
|
||||
|
||||
#### FMaterialAttributes
|
||||
MaterialTemplate.ush有一处`/** Material declarations */`之后会生成对应FMaterialAttributes结构体,可以在材质编辑器的HLSL中查看生成结果。这与
|
||||
- MaterialAttributeDefinitionMap.cpp:FMaterialAttributeDefinitionMap::InitializeAttributeMap()中定义属性。
|
||||
- HLSLMaterialTranslator.cpp:GetMaterialShaderCode()中的`for (const FGuid& AttributeID : OrderedVisibleAttributes)`:生成对应属性结构体以及属性获取函数。
|
||||
|
||||
#### DerivativeAutogen.GenerateUsedFunctions()
|
||||
```c++
|
||||
{
|
||||
FString DerivativeHelpers = DerivativeAutogen.GenerateUsedFunctions(*this);
|
||||
FString DerivativeHelpersAndResources = DerivativeHelpers + ResourcesString;
|
||||
//LazyPrintf.PushParam(*ResourcesString);
|
||||
LazyPrintf.PushParam(*DerivativeHelpersAndResources);
|
||||
}
|
||||
```
|
||||
|
||||
#### GetMaterialEmissiveForCS()以及其他函数
|
||||
```c++
|
||||
if (bCompileForComputeShader)
|
||||
{
|
||||
LazyPrintf.PushParam(*GenerateFunctionCode(CompiledMP_EmissiveColorCS, BaseDerivativeVariation));
|
||||
}
|
||||
else
|
||||
{
|
||||
LazyPrintf.PushParam(TEXT("return 0"));
|
||||
}
|
||||
|
||||
{
|
||||
FLinearColor Extinction = Material->GetTranslucentMultipleScatteringExtinction();
|
||||
LazyPrintf.PushParam(*FString::Printf(TEXT("return MaterialFloat3(%.5f, %.5f, %.5f)"), Extinction.R, Extinction.G, Extinction.B));
|
||||
}
|
||||
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetOpacityMaskClipValue()));
|
||||
{
|
||||
const FDisplacementScaling DisplacementScaling = Material->GetDisplacementScaling();
|
||||
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), FMath::Max(0.0f, DisplacementScaling.Magnitude)));
|
||||
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), FMath::Clamp(DisplacementScaling.Center, 0.0f, 1.0f)));
|
||||
}
|
||||
|
||||
LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_WorldPositionOffset, BaseDerivativeVariation) : TEXT("return Parameters.MaterialAttributes.WorldPositionOffset"));
|
||||
LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(CompiledMP_PrevWorldPositionOffset, BaseDerivativeVariation) : TEXT("return 0.0f"));
|
||||
LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_CustomData0, BaseDerivativeVariation) : TEXT("return 0.0f"));
|
||||
LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_CustomData1, BaseDerivativeVariation) : TEXT("return 0.0f"));
|
||||
```
|
||||
%.5f:表示按浮点数输出,小数点后面取5位其余的舍弃;例如:5/2 “%.5f”输出为:2.50000
|
||||
|
||||
#### MaterialCustomizedUVs & CustomInterpolators
|
||||
- `for (uint32 CustomUVIndex = 0; CustomUVIndex < NumUserTexCoords; CustomUVIndex++)`
|
||||
- `for (UMaterialExpressionVertexInterpolator* Interpolator : CustomVertexInterpolators`
|
||||
|
||||
### 添加ToonDataAssetID 与 ToonOutlineDataAssetID笔记
|
||||
1. FMaterialRenderProxy::UpdateDeferredCachedUniformExpressions()
|
||||
2. FMaterialRenderProxy::EvaluateUniformExpressions()
|
||||
3. FUniformExpressionSet::FillUniformBuffer()
|
||||
4. EvaluatePreshader()
|
||||
5. EvaluateParameter()
|
||||
6. Context.MaterialRenderProxy->GetParameterValue()
|
||||
|
||||
可以看得出关键数据在UniformExpressionSet中,这里的ParameterIndex则通过`EvaluateParameter(Stack, UniformExpressionSet, ReadPreshaderValue<uint16>(Data), Context);`进行计算。
|
||||
```c++
|
||||
const FMaterialNumericParameterInfo& Parameter = UniformExpressionSet->GetNumericParameter(ParameterIndex);
|
||||
bool bFoundParameter = false;
|
||||
|
||||
// First allow proxy the chance to override parameter
|
||||
if (Context.MaterialRenderProxy)
|
||||
{
|
||||
FMaterialParameterValue ParameterValue;
|
||||
if (Context.MaterialRenderProxy->GetParameterValue(Parameter.ParameterType, Parameter.ParameterInfo, ParameterValue, Context))
|
||||
{
|
||||
Stack.PushValue(ParameterValue.AsShaderValue());
|
||||
bFoundParameter = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FMaterialInstanceResource::GetParameterValue(EMaterialParameterType Type, const FHashedMaterialParameterInfo& ParameterInfo, FMaterialParameterValue& OutValue, const FMaterialRenderContext& Context) const
|
||||
{
|
||||
checkSlow(IsInParallelRenderingThread());
|
||||
|
||||
bool bResult = false;
|
||||
|
||||
// Check for hard-coded parameters
|
||||
if (Type == EMaterialParameterType::Scalar && ParameterInfo.Name == GetSubsurfaceProfileParameterName())
|
||||
{
|
||||
check(ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter);
|
||||
const USubsurfaceProfile* MySubsurfaceProfileRT = GetSubsurfaceProfileRT();
|
||||
OutValue = GetSubsurfaceProfileId(MySubsurfaceProfileRT);
|
||||
bResult = true;
|
||||
}
|
||||
else if (Type == EMaterialParameterType::Scalar && NumSpecularProfileRT() > 0)
|
||||
{
|
||||
for (uint32 It=0,Count=NumSpecularProfileRT();It<Count;++It)
|
||||
{
|
||||
if (ParameterInfo.Name == SpecularProfileAtlas::GetSpecularProfileParameterName(GetSpecularProfileRT(It)))
|
||||
{
|
||||
check(ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter);
|
||||
OutValue = SpecularProfileAtlas::GetSpecularProfileId(GetSpecularProfileRT(It));
|
||||
bResult = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### BasePass EncodeGBufferToMRT/DecodeGBufferDataDirect逻辑笔记
|
||||
主要逻辑位于FShaderCompileUtilities::WriteGBufferInfoAutogen():
|
||||
```c++
|
||||
void FShaderCompileUtilities::WriteGBufferInfoAutogen(EShaderPlatform TargetPlatform, ERHIFeatureLevel::Type FeatureLevel = ERHIFeatureLevel::SM5)
|
||||
{
|
||||
FGBufferParams DefaultParams = FetchGBufferParamsPipeline(TargetPlatform, GBL_Default);
|
||||
FScopeLock MapLock(&GCriticalSection);
|
||||
|
||||
// For now, the logic always calculates the new GBuffer, and if it's the first time, write it, otherwise check it hasn't changed. We are doing this for
|
||||
// debugging, and in the near future it will only calculate the GBuffer on the first time only.
|
||||
FGBufferInfo DefaultBufferInfo = FetchFullGBufferInfo(DefaultParams);
|
||||
FString AutoGenDirectory = GetAutoGenDirectory(TargetPlatform);
|
||||
FString AutogenHeaderFilename = AutoGenDirectory / TEXT("AutogenShaderHeaders.ush");
|
||||
FString AutogenHeaderFilenameTemp = AutoGenDirectory / TEXT("AutogenShaderHeaders_temp.ush");
|
||||
|
||||
if (GLastGBufferIsValid[TargetPlatform])
|
||||
{
|
||||
const bool bSame = IsGBufferInfoEqual(GLastGBufferInfo[TargetPlatform], DefaultBufferInfo);//判断GBufferInfo是否相同,不同则触发断言
|
||||
check(bSame);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLastGBufferIsValid[TargetPlatform] = true;
|
||||
// should cache this properly, and serialize it, but this is a temporary fix.
|
||||
GLastGBufferInfo[TargetPlatform] = DefaultBufferInfo;
|
||||
|
||||
FString OutputFileData;
|
||||
OutputFileData += TEXT("// Copyright Epic Games, Inc. All Rights Reserved.\n");
|
||||
OutputFileData += TEXT("\n");
|
||||
OutputFileData += TEXT("#pragma once\n");
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += TEXT("#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5\n");
|
||||
OutputFileData += TEXT("float SampleDeviceZFromSceneTexturesTempCopy(float2 UV)\n");
|
||||
OutputFileData += TEXT("{\n");
|
||||
OutputFileData += TEXT("\treturn SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UV, 0).r;\n");
|
||||
OutputFileData += TEXT("}\n");
|
||||
OutputFileData += TEXT("#endif\n");
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += TEXT("#ifndef GBUFFER_LAYOUT\n");
|
||||
OutputFileData += TEXT("#define GBUFFER_LAYOUT 0\n");
|
||||
OutputFileData += TEXT("#endif\n");
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
for (uint32 Layout = 0; Layout < GBL_Num; ++Layout)
|
||||
{
|
||||
FGBufferParams Params = FetchGBufferParamsPipeline(TargetPlatform, (EGBufferLayout)Layout);
|
||||
FGBufferInfo BufferInfo = FetchFullGBufferInfo(Params);
|
||||
|
||||
OutputFileData.Appendf(TEXT("#if GBUFFER_LAYOUT == %u\n\n"), Layout);
|
||||
OutputFileData += CreateGBufferEncodeFunction(BufferInfo);
|
||||
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += CreateGBufferDecodeFunctionDirect(BufferInfo);
|
||||
|
||||
OutputFileData += TEXT("\n");
|
||||
//OutputFileData += TEXT("#if SHADING_PATH_DEFERRED\n");
|
||||
OutputFileData += TEXT("#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5\n");
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += CreateGBufferDecodeFunctionVariation(BufferInfo, EGBufferDecodeType::CoordUV, FeatureLevel);
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += CreateGBufferDecodeFunctionVariation(BufferInfo, EGBufferDecodeType::CoordUInt, FeatureLevel);
|
||||
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += CreateGBufferDecodeFunctionVariation(BufferInfo, EGBufferDecodeType::SceneTextures, FeatureLevel);
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += CreateGBufferDecodeFunctionVariation(BufferInfo, EGBufferDecodeType::SceneTexturesLoad, FeatureLevel);
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += TEXT("#endif\n");
|
||||
OutputFileData += TEXT("\n");
|
||||
|
||||
OutputFileData += TEXT("#endif\n");
|
||||
OutputFileData += TEXT("\n");
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
写入内容与这2句获取的FGbufferInfo有关:`FGBufferParams Params = FetchGBufferParamsPipeline(TargetPlatform, (EGBufferLayout)Layout);`和`FGBufferInfo BufferInfo = FetchFullGBufferInfo(Params);`
|
||||
|
||||
|
||||
![[ShaderGenerationUtil_CreateGBufferEncodeFunction.png|1200]]
|
||||
|
||||
## 是否需要Toon
|
||||
在材质中:
|
||||
```c++
|
||||
FMaterialRelevance UMaterialInterface::GetRelevance_Internal(const UMaterial* Material, ERHIFeatureLevel::Type InFeatureLevel) const
|
||||
{
|
||||
if(Material)
|
||||
{
|
||||
//YivanLee's Modify 这里仅仅针对人物,因为它决定了是否开启ToonGBuffer,但是对于ToonLevel,ToonFoliage,ToonGrass这里并不需要开启
|
||||
bool bUseToonData = MaterialResource->GetShadingModels().HasAnyShadingModel({ MSM_ToonStandard, MSM_ToonSkin, MSM_ToonHair, MSM_ToonFace, MSM_ToonEyeBrow });
|
||||
}
|
||||
|
||||
···
|
||||
MaterialRelevance.bUsesToonData = bUseToonData;
|
||||
···
|
||||
}
|
||||
```
|
||||
|
||||
在渲染管线中:
|
||||
```c++
|
||||
//RenderUtils.cpp
|
||||
bool IsUsingToonRendering(const FStaticShaderPlatform Platform)
|
||||
{
|
||||
static FShaderPlatformCachedIniValue<int32> PerPlatformCVar(TEXT("r.ToonRendering.Enable"));
|
||||
if (IsMobilePlatform(Platform) || IsForwardShadingEnabled(Platform))//目前不考虑VR与移动端
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (PerPlatformCVar.Get(Platform) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsUsingToonOutline(const FStaticShaderPlatform Platform)
|
||||
{
|
||||
static FShaderPlatformCachedIniValue<int32> PerPlatformCVar(TEXT("r.ToonRendering.ToonOutline"));
|
||||
return (PerPlatformCVar.Get(Platform) == 1) && IsUsingToonRendering(Platform);
|
||||
}
|
||||
|
||||
bool IsUsingToonRimLighting(const FStaticShaderPlatform Platform)
|
||||
{
|
||||
static FShaderPlatformCachedIniValue<int32> PerPlatformCVar(TEXT("r.ToonRendering.ToonRimLighting"));
|
||||
return (PerPlatformCVar.Get(Platform) == 1) && IsUsingToonRendering(Platform);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
李兄的ToonBuffer判断逻辑:
|
||||
```c++
|
||||
bool FDeferredShadingSceneRenderer::ShouldRenderToonDataPass() const
|
||||
{
|
||||
if (!SupportsToonDataMaterials(FeatureLevel, ShaderPlatform))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (IsForwardShadingEnabled(GetFeatureLevelShaderPlatform(FeatureLevel)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (auto& View : Views)
|
||||
{
|
||||
if (View.ShouldRenderView() && View.ParallelMeshDrawCommandPasses[EMeshPass::ToonDataPass].HasAnyDraw())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
## Toon PerObjectGBufferData具体功能表
|
||||
从3开始,0、1、2已被占用。
|
||||
- ?
|
||||
|
||||
## ToonBufferData
|
||||
- ToonObjectID
|
||||
|
||||
```c++
|
||||
struct FSceneDataIntermediates
|
||||
{
|
||||
uint PrimitiveId;
|
||||
uint InstanceId;
|
||||
uint ViewIndex;
|
||||
uint CullingFlags;
|
||||
// Index from which we load the instance info, needed for the
|
||||
uint InstanceIdLoadIndex;
|
||||
FInstanceSceneData InstanceData;
|
||||
FPrimitiveSceneData Primitive;
|
||||
};
|
||||
|
||||
struct FVertexFactoryIntermediatesCommon
|
||||
{
|
||||
/** Cached primitive and instance data */
|
||||
FSceneDataIntermediates SceneData;
|
||||
#if USE_INSTANCING || USE_INSTANCE_CULLING
|
||||
FVertexFactoryInstanceInput InstanceInput;
|
||||
#endif
|
||||
#if USE_SPLINEDEFORM
|
||||
FSplineMeshShaderParams SplineMeshParams;
|
||||
#endif
|
||||
};
|
||||
|
||||
FPrimitiveSceneData GetPrimitiveData(FVertexFactoryIntermediatesCommon Intermediates)
|
||||
{
|
||||
return Intermediates.SceneData.Primitive;
|
||||
}
|
||||
```
|
||||
|
||||
## 高光
|
||||
- PBR高光(使用Roughness控制是否可行?是否需要传入GBuffer一个Mask贴图)
|
||||
- 自定义高光:高光贴图、高光颜色、参数化高光形状、多层高光
|
||||
|
||||
# BasePassPixelShader
|
||||
Velocity相关代码段:
|
||||
```c++
|
||||
#if USES_GBUFFER
|
||||
// -0.5 .. 0.5, could be optimzed as lower quality noise would be sufficient
|
||||
float QuantizationBias = PseudoRandom( MaterialParameters.SvPosition.xy ) - 0.5f;
|
||||
|
||||
GBuffer.IndirectIrradiance = IndirectIrradiance;
|
||||
|
||||
// this is the new encode, the older encode is the #else, keeping it around briefly until the new version is confirmed stable.
|
||||
#if 1
|
||||
{
|
||||
// change this so that we can pack everything into the gbuffer, but leave this for now
|
||||
#if GBUFFER_HAS_DIFFUSE_SAMPLE_OCCLUSION
|
||||
GBuffer.GenericAO = float(GBuffer.DiffuseIndirectSampleOcclusion) * (1.0f / 255.0f);
|
||||
#elif ALLOW_STATIC_LIGHTING
|
||||
// No space for AO. Multiply IndirectIrradiance by AO instead of storing.
|
||||
GBuffer.GenericAO = EncodeIndirectIrradiance(GBuffer.IndirectIrradiance * GBuffer.GBufferAO) + QuantizationBias * (1.0 / 255.0); // Stationary sky light path
|
||||
#else
|
||||
GBuffer.GenericAO = GBuffer.GBufferAO; // Movable sky light path
|
||||
#endif
|
||||
|
||||
EncodeGBufferToMRT(Out, GBuffer, QuantizationBias);
|
||||
|
||||
if (GBuffer.ShadingModelID == SHADINGMODELID_UNLIT && !STRATA_ENABLED) // Do not touch what strata outputs
|
||||
{
|
||||
Out.MRT[1] = 0;
|
||||
SetGBufferForUnlit(Out.MRT[2]);
|
||||
Out.MRT[3] = 0;
|
||||
Out.MRT[GBUFFER_HAS_VELOCITY ? 5 : 4] = 0;
|
||||
Out.MRT[GBUFFER_HAS_VELOCITY ? 6 : 5] = 0;
|
||||
}
|
||||
|
||||
#if SINGLE_LAYER_WATER_SEPARATED_MAIN_LIGHT
|
||||
// In deferred, we always output the directional light in a separated buffer.
|
||||
// This is used to apply distance field shadows or light function to the main directional light.
|
||||
// Strata also writes it through MRT because this is faster than through UAV.
|
||||
#if STRATA_ENABLED && STRATA_INLINE_SINGLELAYERWATER
|
||||
Out.MRT[(GBUFFER_HAS_VELOCITY ? 2 : 1) + (GBUFFER_HAS_PRECSHADOWFACTOR ? 1 : 0)] = float4(SeparatedWaterMainDirLightLuminance * View.PreExposure, 1.0f);
|
||||
#else
|
||||
if (GBuffer.ShadingModelID == SHADINGMODELID_SINGLELAYERWATER)
|
||||
{
|
||||
Out.MRT[(GBUFFER_HAS_VELOCITY ? 6 : 5) + (GBUFFER_HAS_PRECSHADOWFACTOR ? 1 : 0)] = float4(SeparatedWaterMainDirLightLuminance * View.PreExposure, 1.0f);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
# 顶点色
|
||||
## 蓝色协议
|
||||
用于存储一些低精度数据,插值即可
|
||||
- 顶点色:
|
||||
- R:阴影区域控制(强度) 0~1
|
||||
- G:描边宽度
|
||||
- B:ToonAO
|
||||
- 第二套顶点色(UV Channel1):
|
||||
- R:深度偏移
|
||||
- G:用来区分内轮廓不同部位的ID
|
||||
|
||||
蓝色协议的R:阴影区域标记 与 B:AO,而罪恶装备使用贴图来传递信息。
|
||||
## 罪恶装备
|
||||
对阴影判断阈值的偏移。(见前面着色部分,顶点AO+手绘修正)
|
||||
R:阴影偏移
|
||||
G:轮廓线根据与相机的距离扩大多少的系数
|
||||
B:等高线 Z 轴偏移值
|
||||
|
||||
# 罪恶装备
|
||||
8,G为阴影控(AO),R为高光强度参数,金属和光滑材质的部分设置的更大一些。B通道:用于照明控制。最大值为高光,反之,值越小高光越淡。
|
||||

|
||||
|
||||
https://zhuanlan.zhihu.com/p/360229590一文中介绍了崩坏3与原神的计算方式
|
||||
|
||||
崩坏3的LightMap计算方式:
|
||||
```c++
|
||||
half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv.xy);
|
||||
half4 LightMapColor = SAMPLE_TEXTURE2D(_LightMap, sampler_LightMap, input.uv.xy);
|
||||
half3 ShadowColor = baseColor.rgb * _ShadowMultColor.rgb;
|
||||
half3 DarkShadowColor = baseColor.rgb * _DarkShadowMultColor.rgb;
|
||||
|
||||
//如果SFactor = 0,ShallowShadowColor为一级阴影色,否则为BaseColor。
|
||||
float SWeight = (LightMapColor.g * input.color.r + input.lambert) * 0.5 + 1.125;
|
||||
float SFactor = floor(SWeight - _ShadowArea);
|
||||
half3 ShallowShadowColor = SFactor * baseColor.rgb + (1 - SFactor) * ShadowColor.rgb;
|
||||
```
|
||||
|
||||
二级阴影计算:
|
||||
```c++
|
||||
//如果SFactor = 0,DarkShadowColor为二级阴影色,否则为一级阴影色。
|
||||
SFactor = floor(SWeight - _DarkShadowArea);
|
||||
DarkShadowColor = SFactor * (_FixDarkShadow * ShadowColor + (1 - _FixDarkShadow) * ShallowShadowColor) + (1 - SFactor) * DarkShadowColor;
|
||||
|
||||
// 平滑阴影边缘
|
||||
half rampS = smoothstep(0, _ShadowSmooth, input.lambert - _ShadowArea);
|
||||
half rampDS = smoothstep(0, _DarkShadowSmooth, input.lambert - _DarkShadowArea);
|
||||
ShallowShadowColor.rgb = lerp(ShadowColor, baseColor.rgb, rampS);
|
||||
DarkShadowColor.rgb = lerp(DarkShadowColor.rgb, ShadowColor, rampDS);
|
||||
|
||||
//如果SFactor = 0,FinalColor为二级阴影,否则为一级阴影。
|
||||
SFactor = floor(LightMapColor.g * input.color.r + 0.9f);
|
||||
half4 FinalColor;
|
||||
FinalColor.rgb = SFactor * ShallowShadowColor + (1 - SFactor) * DarkShadowColor;
|
||||
```
|
||||
|
||||
|
||||
**罪恶装备**:
|
||||
对阴影判断阈值的偏移。(见前面着色部分,顶点AO+手绘修正)
|
||||
G : 轮廓线根据与相机的距离扩大多少的系数
|
||||
B : 等高线 Z 轴偏移值
|
||||
A : 轮廓厚度系数。0.5为标准,1为最大厚度,0为无等高线
|
||||
|
||||
# 蓝色协议
|
||||
[[蓝色协议的方案]]
|
||||
|
||||
# 米哈游
|
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Untitled
|
||||
date: 2025-01-15 16:33:08
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
在渲染线程使用UObject会导致崩溃,所以使用将部分参数传递到MaterialRenderProxy的方式来规避。但这样还需要解决UToonDataAsset不会触发Material刷新MaterialRenderProxy的问题。
|
||||
|
||||
# 思路
|
||||
1. UMaterialInstance::PreSave()
|
114
03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/ToonShaderModel.md
Normal file
114
03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/ToonShaderModel.md
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: ToonShaderModel
|
||||
date: 2023-12-18 10:00:34
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
# ToonStandard(Cel打底)
|
||||
## Diffuse
|
||||
```c++
|
||||
//Lighting.Diffuse *= AreaLight.FalloffColor * (Falloff * HalfLambert);
|
||||
//TODO:添加阴影过渡效果。
|
||||
//ToonShadow添加类型:1.PBR(只能调整过渡颜色) Lighting.Diffuse = DiffuseColor * (1 / PI) * AreaLight.FalloffColor * (Falloff * HalfLambert); 2.ShadowColorTexture(兼容RampTexture效果也就是原神的效果),还需要添加ShadowColorIntensity(后需要改成曝光相关额东西)。Lighting.Diffuse = DiffuseColor * (1 / PI);
|
||||
//ToonLighting添加类型:1.PBR(多光源模式)2.主光有效,针对cel。
|
||||
//---------------------------------------------------------
|
||||
//Specular
|
||||
//ToonSpecular添加类型:1.PBR 2. 各项异性(defaultLit)3. 各项异性头发 4. 自定义高光大小与过渡效果 https://zhuanlan.zhihu.com/p/361918341 5.自定义高光形状贴图 https://zhuanlan.zhihu.com/p/640258070 https://github.com/AnCG7/URPShaderCodeSample/blob/891034b3fa6e838e2b231390755479f0f649f181/Assets/Shaders/NPR/Cartoon/Stylized%20Highlight%20(Transform).shader#L2
|
||||
```
|
||||
|
||||
**ToonShadow**:
|
||||
再实现阴影偏移以及阴影羽化效果的基础上。
|
||||
1. PBR:兼容UE默认阴影效果。只能调节阴影过渡颜色。使用的公式为: `Lighting.Diffuse = DiffuseColor * (1 / PI) * AreaLight.FalloffColor * (Falloff * HalfLambert);`
|
||||
2. ShadowColorTexture:用于定义ShadowColor(使用贴图指定ShadowColor)
|
||||
|
||||
**ToonLighting**:
|
||||
1. PBR:UE默认光照,多光源模式。
|
||||
2. 仅主光有效:针对cel。
|
||||
|
||||
## Specular
|
||||
**ToonSpecular**:
|
||||
1. PBR:UE默认高光效果。
|
||||
2. 各项异性(defaultLit):UE默认的各向异性高光效果。
|
||||
3. 各项异性头发 :**Kajiya-Kay**高光模型。
|
||||
4. 自定义高光大小与过渡效果:https://zhuanlan.zhihu.com/p/361918341
|
||||
5. 自定义高光形状贴图:https://zhuanlan.zhihu.com/p/640258070 https://github.com/AnCG7/URPShaderCodeSample/blob/891034b3fa6e838e2b231390755479f0f649f181/Assets/Shaders/NPR/Cartoon/Stylized%20Highlight%20(Transform).shader#L2
|
||||
|
||||
## 罪恶装备渲染效果还原
|
||||
- 原始演讲视频:https://www.bilibili.com/video/BV1Ps411C7mw/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- 原始演讲PPT:https://www.ggxrd.com/Motomura_Junya_GuiltyGearXrd.pdf
|
||||
- [【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(1)](https://www.cnblogs.com/TracePlus/p/4205798.html "发布于 2015-01-06 12:56")
|
||||
- [【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(2)](https://www.cnblogs.com/TracePlus/p/4205834.html "发布于 2015-01-06 13:23")
|
||||
- [【翻译】西川善司的「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,后篇](https://www.cnblogs.com/TracePlus/p/4205978.html "发布于 2015-01-06 14:23")
|
||||
- 知乎文章
|
||||
1. [x] ***https://zhuanlan.zhihu.com/p/631214546
|
||||
2. [ ] https://zhuanlan.zhihu.com/p/436850004
|
||||
3. https://zhuanlan.zhihu.com/p/513598386
|
||||
4. https://zhuanlan.zhihu.com/p/493802718
|
||||
5. [x] https://zhuanlan.zhihu.com/p/513228315
|
||||
1. 其他人的Demo视频,角色胜利动画。
|
||||
1. [ ] [Guraaaa的技术美术作品集](https://www.bilibili.com/video/BV1xx4y1T7Er/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=11)
|
||||
|
||||
### 贴图
|
||||
- base:基础色
|
||||
- [x] rgb:亮面颜色
|
||||
- [x] **a**:basecolor的A通道用于区分脸、身体区域和头发区域。BGT会兜帽内侧与脸都是黑色、头发是灰色;MAY头发是灰色,身体与头是黑色;RAM、JKO身体与头发都是黑色;SOL正脸、头发是黑色,侧脸是白色。
|
||||
- ShadowMap (SSS)
|
||||
- [x] rgb:暗面颜色
|
||||
- **a**:?
|
||||
- ILM
|
||||
- [x] **ilm(R)**:高光强度 ?***高光类型(待验证)***,根绝高光类型来设置不同的强度数值。
|
||||
- `0` 无高光、无边缘光。为角色的眼睛或者眉毛,有个例外:JKO的头发。
|
||||
- `(0~50]` 计算高光?、边缘光。主要的渲染区域,为角色的主要衣服与皮肤。
|
||||
- `(50~100]` 贴图绘制高光(ILM.b)、有边缘光。为角色的**头发**与衣服。
|
||||
- `(100~150]`计算高光、无边缘光。为角色身体上的金属边缘或者刮痕,SOL的武器。
|
||||
- `(150~200]`贴图绘制高光、无边缘光。为角色身体上大部分的金属物件。SOL腰带上的铁环与肩膀处的铁环。
|
||||
- `(200~255]`
|
||||
- [x] ilm(G):阴影与高光Offset
|
||||
- [x] ilm(B):高光遮罩 ? 高光类型Mask。
|
||||
- 目前猜测,计算高光,使用数值调整高光大小,类似Roughness;高光Mask直接使用这个Mask。
|
||||
- `0`:无高光
|
||||
- `128`:计算高光区域。
|
||||
- `(128,255]`:(128,255) => (0,1) 高光Mask
|
||||
- [x] ilm(A):本衬线(基于UV制作,风格化内描边,原理参考先前文章),内描边Mask。
|
||||
- [x] detail(texcoord[1]):细节贴图,第二套UV。
|
||||
- [x] decal:贴花,单独一个材质。
|
||||
- VertexColor
|
||||
- [x] VertexColor(R):AO
|
||||
- [x] VertexColor(G):猜测是轮廓线的PixelDepthOffset,以此解决角色叠在一起可能会出现的问题。
|
||||
- ~~Xrd翻译文章:对应到Camera的距离,轮廓线的在哪个范围膨胀的系数。~~
|
||||
- [x] VertexColor(B):描边粗细,也就是Backface Outline的挤出数值。
|
||||
- Xrd翻译文章:轮廓线的Z Offset 值
|
||||
- ~~VertexColor(A) ~~:不存在
|
||||
- Xrd翻译文章:轮廓线的粗细系数。0.5是标准,1是最粗,0的话就没有轮廓线。
|
||||
- [x] OLM:皮肤Mask,部分角色RAM、JKO还会包括头发。颜色数值不一定相同(RAM就不同)。
|
||||
|
||||
### 边缘光
|
||||
1. 主光的方向不确定,但边缘光的方向是固定的。
|
||||
2. 使用NoL计算边缘光;不同区域的边缘光使用某个参数控制宽度(估计是裁边)。
|
||||
3.
|
||||
![[边缘光.png]]
|
||||
|
||||
![[BGT边缘光.png|500]]
|
||||
|
||||
![[BGT边缘光2.png|500]]
|
||||
|
||||
|
||||
![[BGT边缘光3.png|500]]
|
||||
|
||||
|
||||
![[BGT边缘光4——大佛.png|500]]
|
||||
|
||||
![[BGT边缘光5.png|500]]
|
||||
|
||||
### TODO
|
||||
- [x] 实现AmbientOcclusion叠加阴影效果。
|
||||
- [x] 添加ToonDataAsset 控制是否接受阴影选项。
|
||||
- [x] 使用代理Shadow模型来渲染阴影。
|
||||
|
||||
## UnityChan
|
||||
|
||||
## 蓝色协议
|
||||
|
||||
# 厚涂 ShaderModel(通过修改预积分ShaderModel)
|
18
03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/Toon多光源参考.md
Normal file
18
03-UnrealEngine/卡通渲染相关资料/渲染功能/ShaderModel/Toon多光源参考.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: Toon多光源参考
|
||||
date: 2025-03-27 19:01:13
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
- [【UE5】卡通渲染着色篇3:多光源](https://zhuanlan.zhihu.com/p/717533663)
|
||||
- DirectionalLight
|
||||
- 主要是通过判断所有灯光的Forward Shading Priority与亮度,取得主光。
|
||||
- 之后在FDeferredLightPS中添加一个判断是否是主光的变体,并进行设置即可。
|
||||
- 也可以通过FlattenNormal来减少高频信息。
|
||||
- PointLight
|
||||
- 可以通过FlattenNormal(ShadingModels.ush)来减少点光源计算中的高频细节
|
||||
- YivanLee的多光源方案
|
||||
- ShadingModels.ush中只渲染光影(不渲染颜色),用于合并多光源光影结果。
|
||||
- 在Lighting Pass之后添加一个LightingPostProcess Pass,根据合并的光影采样Ramp渲染最终光照结果。
|
@@ -0,0 +1,140 @@
|
||||
---
|
||||
title: 未命名
|
||||
date: 2024-05-10 16:30:16
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
PostProcesss
|
||||
```c++
|
||||
void AddPostProcessingPasses(
|
||||
FRDGBuilder& GraphBuilder,
|
||||
const FViewInfo& View,
|
||||
int32 ViewIndex,
|
||||
FSceneUniformBuffer& SceneUniformBuffer,
|
||||
bool bAnyLumenActive,
|
||||
bool bLumenGIEnabled,
|
||||
EReflectionsMethod ReflectionsMethod,
|
||||
const FPostProcessingInputs& Inputs,
|
||||
const Nanite::FRasterResults* NaniteRasterResults,
|
||||
FInstanceCullingManager& InstanceCullingManager,
|
||||
FVirtualShadowMapArray* VirtualShadowMapArray,
|
||||
FLumenSceneFrameTemporaries& LumenFrameTemporaries,
|
||||
const FSceneWithoutWaterTextures& SceneWithoutWaterTextures,
|
||||
FScreenPassTexture TSRMoireInput)
|
||||
{
|
||||
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderPostProcessing);
|
||||
QUICK_SCOPE_CYCLE_COUNTER(STAT_PostProcessing_Process);
|
||||
|
||||
check(IsInRenderingThread());
|
||||
check(View.VerifyMembersChecks());
|
||||
Inputs.Validate();
|
||||
|
||||
FScene* Scene = View.Family->Scene->GetRenderScene();
|
||||
|
||||
const FIntRect PrimaryViewRect = View.ViewRect;
|
||||
|
||||
const FSceneTextureParameters SceneTextureParameters = GetSceneTextureParameters(GraphBuilder, Inputs.SceneTextures);
|
||||
|
||||
const FScreenPassRenderTarget ViewFamilyOutput = FScreenPassRenderTarget::CreateViewFamilyOutput(Inputs.ViewFamilyTexture, View);
|
||||
const FScreenPassTexture SceneDepth(SceneTextureParameters.SceneDepthTexture, PrimaryViewRect);
|
||||
const FScreenPassTexture CustomDepth(Inputs.CustomDepthTexture, PrimaryViewRect);
|
||||
const FScreenPassTexture Velocity(SceneTextureParameters.GBufferVelocityTexture, PrimaryViewRect);
|
||||
const FScreenPassTexture BlackDummy(GSystemTextures.GetBlackDummy(GraphBuilder));
|
||||
const FTranslucencyPassResources& PostDOFTranslucencyResources = Inputs.TranslucencyViewResourcesMap.Get(ETranslucencyPass::TPT_TranslucencyAfterDOF);
|
||||
const FTranslucencyPassResources& PostMotionBlurTranslucencyResources = Inputs.TranslucencyViewResourcesMap.Get(ETranslucencyPass::TPT_TranslucencyAfterMotionBlur);
|
||||
```
|
||||
|
||||
# BasePass
|
||||
```c++
|
||||
#if WITH_EDITOR
|
||||
if (ViewFamily.EngineShowFlags.Wireframe)
|
||||
{
|
||||
checkf(ExclusiveDepthStencil.IsDepthWrite(), TEXT("Wireframe base pass requires depth-write, but it is set to read-only."));
|
||||
|
||||
BasePassTextureCount = 1;
|
||||
BasePassTextures[0] = SceneTextures.EditorPrimitiveColor;
|
||||
BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);
|
||||
|
||||
BasePassDepthTexture = SceneTextures.EditorPrimitiveDepth;
|
||||
|
||||
auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
|
||||
PassParameters->RenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::EClear, BasePassTexturesView);
|
||||
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::EClear, ExclusiveDepthStencil);
|
||||
|
||||
GraphBuilder.AddPass(RDG_EVENT_NAME("WireframeClear"), PassParameters, ERDGPassFlags::Raster, [](FRHICommandList&) {});
|
||||
}
|
||||
#endif
|
||||
|
||||
// Render targets bindings should remain constant at this point.
|
||||
FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView);
|
||||
BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);
|
||||
|
||||
FForwardBasePassTextures ForwardBasePassTextures{};
|
||||
|
||||
if (bForwardShadingEnabled)
|
||||
{
|
||||
ForwardBasePassTextures.SceneDepthIfResolved = SceneTextures.Depth.IsSeparate() ? SceneTextures.Depth.Resolve : nullptr;
|
||||
ForwardBasePassTextures.ScreenSpaceAO = SceneTextures.ScreenSpaceAO;
|
||||
ForwardBasePassTextures.ScreenSpaceShadowMask = ForwardShadowMaskTexture;
|
||||
}
|
||||
else if (!ExclusiveDepthStencil.IsDepthWrite())
|
||||
{
|
||||
// If depth write is not enabled, we can bound the depth texture as read only
|
||||
ForwardBasePassTextures.SceneDepthIfResolved = SceneTextures.Depth.Resolve;
|
||||
}
|
||||
ForwardBasePassTextures.bIs24BitUnormDepthStencil = ForwardBasePassTextures.SceneDepthIfResolved ? GPixelFormats[ForwardBasePassTextures.SceneDepthIfResolved->Desc.Format].bIs24BitUnormDepthStencil : 1;
|
||||
|
||||
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_BasePass));
|
||||
RenderBasePassInternal(GraphBuilder, SceneTextures, BasePassRenderTargets, BasePassDepthStencilAccess, ForwardBasePassTextures, DBufferTextures, bDoParallelBasePass, bRenderLightmapDensity, InstanceCullingManager, bNaniteEnabled, NaniteRasterResults);
|
||||
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_AfterBasePass));
|
||||
|
||||
|
||||
|
||||
FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView);
|
||||
BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);
|
||||
|
||||
RenderBasePassInternal(GraphBuilder, SceneTextures, BasePassRenderTargets, BasePassDepthStencilAccess, ForwardBasePassTextures, DBufferTextures, bDoParallelBasePass, bRenderLightmapDensity, InstanceCullingManager, bNaniteEnabled, NaniteRasterResults);
|
||||
```
|
||||
## 读取GBufferTexture
|
||||
位于SceneRendering.h
|
||||
```c++
|
||||
FORCEINLINE FSceneTextures& GetActiveSceneTextures() { return ViewFamily.GetSceneTextures(); }
|
||||
```
|
||||
|
||||
考虑使用形参
|
||||
- const FSceneTextures* SceneTextures
|
||||
- const FSceneTextures& SceneTextures
|
||||
- FSceneTextures& SceneTextures
|
||||
|
||||
# UE5.4的FScreenTransform计算
|
||||
参考:VisualizeMotionVectors.cpp
|
||||
|
||||
- FScreenTransform::SvPositionToViewportUV(Output.ViewRect):**SvPosition => ViewportUV**
|
||||
- `FScreenTransform SvPositionToViewportUV = FScreenTransform::SvPositionToViewportUV(Output.ViewRect);`
|
||||
- FScreenTransform::ViewportUVToScreenPos():**ViewportUV => ScreenPos**
|
||||
- FScreenTransform::ChangeTextureBasisFromTo():坐标转换。比如下面的坐标是**ViewportUV => TextureUV**
|
||||
- ```FScreenTransform::ChangeTextureBasisFromTo(Inputs.SceneColor, FScreenTransform::ETextureBasis::ViewportUV, FScreenTransform::ETextureBasis::TextureUV);```
|
||||
- **SvPosition => ScreenPos**:SvPositionToViewportUV * FScreenTransform::ViewportUVToScreenPos
|
||||
|
||||
这是用于重采样后2个RT大小不一样而进行的计算。
|
||||
|
||||
# OutlinePass
|
||||
|
||||
## 距离对于后处理深度描边的关系
|
||||
需要针对极近距离进行处理。
|
||||
- 最大深度采样值的最小值为100
|
||||
|
||||
| 距离 | 最小深度采样 | 最大深度采样 |
|
||||
| :-----: | :----: | :----: |
|
||||
| 100cm | 1 | 10 |
|
||||
| 200cm | 2 | 20 |
|
||||
| 500cm | 5 | 50 |
|
||||
| 1000cm | | 100 |
|
||||
| 2000cm | | |
|
||||
| 5000cm | | |
|
||||
| 10000cm | | |
|
||||
# 问题记录
|
||||
## 处于FarDepthValue的Outline被裁剪的问题
|
||||
- SkyAtmosphere.usf中,会将天空球渲染在深度为FarDepthValue的像素上,这样会将一些Outline覆盖掉。
|
||||
- HeightFogPixelShader.usf中,会通过判断**DeviceZ != 0.0** 来调整渲染结果,绘制方式PSO.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_SourceAlpha>::GetRHI();
|
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: 未命名
|
||||
date: 2025-03-27 17:37:02
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# Toon Lumen
|
||||
## 抹平法线思路
|
||||
### ViewVector Fix WorldNormal
|
||||
参考:
|
||||
- [【UE5】环境光与GI 2:抹平法线](https://zhuanlan.zhihu.com/p/25839790454)
|
||||
|
||||
在`LumenScreenProbeGather.usf`中对法线进行抹平处理。其Pass位于
|
||||
- DiffuseIndirectAndAO
|
||||
- LumenScreenProbeGather
|
||||
- Integrate
|
||||
- SimpleDiffuse/SupportImportanceSampleBRDF?/SupportAll?
|
||||
|
||||
在如下位置添加代码:
|
||||
```c++
|
||||
if (IsValid(Material))
|
||||
{
|
||||
const float2 ScreenUV = (Coord.SvPosition + 0.5f) * View.BufferSizeAndInvSize.zw;
|
||||
const float3 WorldPosition = GetWorldPositionFromScreenUV(ScreenUV, Material.SceneDepth);
|
||||
const float3 WorldNormal = Material.WorldNormal;
|
||||
//BlueRose Modify
|
||||
if (Material.ShadingID == SHADINGMODELID_TOONSTANDARD)
|
||||
{
|
||||
//使用Yu-ki016的方法“抹平法线”
|
||||
float3 V = normalize(LWCHackToFloat(PrimaryView.WorldCameraOrigin) - WorldPosition);
|
||||
const uint ToonDataAssetID = GetToonDataAssetIDFromGBuffer(Material.GBufferData);
|
||||
float DiffuseIndirectLightingFlatten = GetDiffuseIndirectLightingFlatten(ToonDataAssetID);
|
||||
Material.WorldNormal = normalize(lerp(WorldNormal, V, DiffuseIndirectLightingFlatten));
|
||||
}
|
||||
//BlueRose Modify End
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
该方法存在一些问题:**当相机围绕角色旋转时,角色身上的 GI变化会比较明显**。
|
||||
|
||||
### 低频化WorldNormal & Spherical Harmonics
|
||||
参考:
|
||||
- [[UFSH2024]用虚幻引擎5为《幻塔》定制高品质动画流程风格化渲染管线 | 晨风 Neverwind 完美世界游戏】【精准空降到 12:55】](https://www.bilibili.com/video/BV1rW2LYvEox/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=775)
|
||||
- [[UFSH2024]用虚幻引擎5为《幻塔》定制高品质动画流程风格化渲染管线 | 晨风 Neverwind 完美世界游戏】 【精准空降到 36:52】](https://www.bilibili.com/video/BV1rW2LYvEox/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=2212)
|
||||
- [ ] 需要实现:法线平滑 => 饱和度平滑。(没说细节,不清楚算法)
|
||||
- [[UFSH2024]用虚幻引擎5为《幻塔》定制高品质动画流程风格化渲染管线 | 晨风 Neverwind 完美世界游戏】【精准空降到 35:09】](https://www.bilibili.com/video/BV1rW2LYvEox/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=2109)
|
||||
- [ ] 小于等于High存储的是一个八面体,提前加一个Pass进行平滑:采样法线方向半球面的平均数值。
|
||||
- [ ] 大于等于Epic存储的是一个三阶球谐,保持1阶球谐数值不变,对2~3阶球谐按照系数进行平滑。并且添加能量补偿,大致与PBR的结果接近。
|
||||
![[Lumen_SHData.png|800]]
|
||||
|
||||
![[Lumen_能量补偿1.png|800]]
|
||||
![[Lumen_能量补偿2.png|800]]
|
||||
UE5中采样球谐贴图方法(ReflectionEnvironmentShared.ush):
|
||||
```c++
|
||||
/**
|
||||
* Computes sky diffuse lighting from the SH irradiance map.
|
||||
* This has the SH basis evaluation and diffuse convolution weights combined for minimal ALU's - see "Stupid Spherical Harmonics (SH) Tricks"
|
||||
*/
|
||||
float3 GetSkySHDiffuse(float3 Normal)
|
||||
{
|
||||
float4 NormalVector = float4(Normal, 1.0f);
|
||||
|
||||
float3 Intermediate0, Intermediate1, Intermediate2;
|
||||
Intermediate0.x = dot(SkyIrradianceEnvironmentMap[0], NormalVector);
|
||||
Intermediate0.y = dot(SkyIrradianceEnvironmentMap[1], NormalVector);
|
||||
Intermediate0.z = dot(SkyIrradianceEnvironmentMap[2], NormalVector);
|
||||
|
||||
float4 vB = NormalVector.xyzz * NormalVector.yzzx;
|
||||
Intermediate1.x = dot(SkyIrradianceEnvironmentMap[3], vB);
|
||||
Intermediate1.y = dot(SkyIrradianceEnvironmentMap[4], vB);
|
||||
Intermediate1.z = dot(SkyIrradianceEnvironmentMap[5], vB);
|
||||
|
||||
float vC = NormalVector.x * NormalVector.x - NormalVector.y * NormalVector.y;
|
||||
Intermediate2 = SkyIrradianceEnvironmentMap[6].xyz * vC;
|
||||
|
||||
// max to not get negative colors
|
||||
return max(0, Intermediate0 + Intermediate1 + Intermediate2);
|
||||
}
|
||||
```
|
||||
|
||||
## Cel适配
|
||||
需要实现Cel多光源方案。
|
||||
|
||||
大致的思路就是Copy Lumen结果,之后在[[Toon多光源参考|Toon多光源Pass]]中提取亮度,再根据亮度采样Ramp计算光影结果。
|
@@ -0,0 +1,834 @@
|
||||
---
|
||||
title: ToonReflection
|
||||
date: 2025-03-20 17:04:16
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 反射功能相关Pass
|
||||
- ReflectionIndirect(None)
|
||||
- [[#ReflectionEnvironmentAndSky]]
|
||||
- DiffuseIndirectAndAO(Lumen/SSR)
|
||||
- LumenReflections
|
||||
- [[#DiffuseIndirectComposite]]
|
||||
|
||||
PS. DiffuseIndirectAndAO Pass会根据 ViewPipelineState.DiffuseIndirectMethod是否为Lumen,出现在Light Pass之前(GI方法为Lumen)或者之后(GI方法为非Lumen)。
|
||||
## ReflectionEnvironmentAndSky
|
||||
位于IndirectLightRendering.cpp的RenderDeferredReflectionsAndSkyLighting() => `AddSkyReflectionPass()`
|
||||
|
||||
当`DiffuseIndirectMethod = EDiffuseIndirectMethod::Lumen`(也就是开启Lumen GI),如果反射方法为Lumen或者SSR则不会执行后续逻辑。
|
||||
|
||||
不开启Lumen GI,反射方法为:
|
||||
- Lumen:`RenderLumenReflections()`
|
||||
- RT Reflection:`RenderRayTracingReflections()`
|
||||
- SSR:`ScreenSpaceRayTracing::RenderScreenSpaceReflections()`
|
||||
|
||||
`RenderDeferredReflectionsAndSkyLighting()`主要执行了:
|
||||
1. SkyLightDiffuse
|
||||
1. RenderDistanceFieldLighting()
|
||||
1. RenderDistanceFieldAOScreenGrid():渲染距离场AO。
|
||||
2. RenderCapsuleShadowsForMovableSkylight():渲染胶囊阴影。
|
||||
2. ReflectionIndirect
|
||||
- RenderLumenReflections()
|
||||
- RenderRayTracingReflections()
|
||||
- RenderScreenSpaceReflections()
|
||||
3. Denoise
|
||||
- Denoiser:IScreenSpaceDenoiser::DenoiseReflections()
|
||||
- TemporalFilter:AddTemporalAAPass()
|
||||
4. RenderDeferredPlanarReflections():合成平面反射结果。
|
||||
5. AddSkyReflectionPass()
|
||||
|
||||
几种反射方式的大致执行逻辑:
|
||||
- LumenReflection
|
||||
1. 输出FRDGTextureRef ReflectionsColor。
|
||||
- SSR与RT
|
||||
1. 输出结果到IScreenSpaceDenoiser::FReflectionsInputs DenoiserInputs的FRDGTextureRef Color。
|
||||
2. 执行对应的降噪算法。
|
||||
3. 结果赋予给FRDGTextureRef ReflectionsColor。
|
||||
- 执行完上述反射方法后,最后执行`AddSkyReflectionPass()`
|
||||
|
||||
FReflectionEnvironmentSkyLightingPS位于/Engine/Private/ReflectionEnvironmentPixelShader.usf的`ReflectionEnvironmentSkyLighting()`。
|
||||
|
||||
### ReflectionEnvironmentSkyLighting
|
||||
```c++
|
||||
void ReflectionEnvironmentSkyLighting(
|
||||
in float4 SvPosition : SV_Position,
|
||||
out float4 OutColor : SV_Target0
|
||||
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
|
||||
, out float3 OutOpaqueRoughRefractionSceneColor : SV_Target1
|
||||
, out float3 OutSubSurfaceSceneColor : SV_Target2
|
||||
#endif
|
||||
)
|
||||
{
|
||||
ResolvedView = ResolveView();
|
||||
//计算获去BufferUV、ScreenPosition
|
||||
uint2 PixelPos = SvPosition.xy;
|
||||
float2 BufferUV = SvPositionToBufferUV(SvPosition);
|
||||
float2 ScreenPosition = SvPositionToScreenPosition(SvPosition).xy;
|
||||
|
||||
OutColor = 0.0f;
|
||||
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
|
||||
OutOpaqueRoughRefractionSceneColor = 0.0f;
|
||||
OutSubSurfaceSceneColor = 0.0f;
|
||||
#endif
|
||||
|
||||
#if STRATA_ENABLED
|
||||
...
|
||||
...
|
||||
#else // STRATA_ENABLED
|
||||
|
||||
// Sample scene textures.
|
||||
FGBufferData GBuffer = GetGBufferDataFromSceneTextures(BufferUV);
|
||||
|
||||
uint ShadingModelID = GBuffer.ShadingModelID;
|
||||
const bool bUnlitMaterial = ShadingModelID == SHADINGMODELID_UNLIT;
|
||||
|
||||
float3 DiffuseColor = GBuffer.DiffuseColor;
|
||||
float3 SpecularColor = GBuffer.SpecularColor;
|
||||
RemapClearCoatDiffuseAndSpecularColor(GBuffer, ScreenPosition, DiffuseColor, SpecularColor);//针对清漆材质进行Diffuse颜色与Specular颜色重新映射
|
||||
|
||||
// Sample the ambient occlusion that is dynamically generated every frame.
|
||||
float AmbientOcclusion = AmbientOcclusionTexture.SampleLevel(AmbientOcclusionSampler, BufferUV, 0).r;
|
||||
|
||||
float3 BentNormal = GBuffer.WorldNormal;
|
||||
#if APPLY_SKY_SHADOWING
|
||||
{
|
||||
BentNormal = UpsampleDFAO(BufferUV, GBuffer.Depth, GBuffer.WorldNormal);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_DYNAMIC_SKY_LIGHT
|
||||
BRANCH
|
||||
if (!bUnlitMaterial) // Only light pixels marked as lit //Unlit材质不会计算动态天光GI的效果。
|
||||
{
|
||||
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, GBuffer.Depth), GBuffer.Depth, 1), View.ScreenToTranslatedWorld).xyz;
|
||||
const float CloudVolumetricAOShadow = GetCloudVolumetricAOShadow(TranslatedWorldPosition);//从体积云 VolumetricCloudShadowMapTexture中取得ShadowFrontDepthKm、MaxOpticalDepth,MeanExtinction,最终计算出体积云阴影。UE5.3该函数没有启用。
|
||||
|
||||
float3 SkyLighting = CloudVolumetricAOShadow * SkyLightDiffuse(GBuffer, AmbientOcclusion, BufferUV, ScreenPosition, BentNormal, DiffuseColor);
|
||||
|
||||
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
|
||||
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(ShadingModelID);
|
||||
LightAccumulator_Add(LightAccumulator, SkyLighting, SkyLighting, 1.0f, bNeedsSeparateSubsurfaceLightAccumulation);
|
||||
OutColor = LightAccumulator_GetResult(LightAccumulator);
|
||||
}
|
||||
|
||||
#endif // ENABLE_DYNAMIC_SKY_LIGHT
|
||||
|
||||
BRANCH
|
||||
if (!bUnlitMaterial && ShadingModelID != SHADINGMODELID_HAIR)//
|
||||
{
|
||||
OutColor.xyz += ReflectionEnvironment(GBuffer, AmbientOcclusion, BufferUV, ScreenPosition, SvPosition, BentNormal, SpecularColor, ShadingModelID);
|
||||
}
|
||||
|
||||
#endif // STRATA_ENABLED
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
### SkyLightDiffuse
|
||||
1. 计算float3 SkyLightingNormal、FSkyLightVisibilityData SkyVisData。
|
||||
2. 计算Normal、ViewVector、NoV。
|
||||
3. 针对制定ShadingModel进行额外计算:
|
||||
1. SHADINGMODELID_TWOSIDED_FOLIAGE:使用Normal反向量取得SkySHDiffuse,在乘以SubsurfaceColor、SkyVisData.SkyDiffuseLookUpMul后累加到结果上。
|
||||
2. SHADINGMODELID_SUBSURFACE、SHADINGMODELID_PREINTEGRATED_SKIN:从GBuffer中提取SubsurfaceColor并累加到结果上。
|
||||
3. SHADINGMODELID_CLOTH:从GBuffer中提取ClothFuzz(SubsurfaceColor)乘以CustomData.a,并累加到结果上。
|
||||
4. SHADINGMODELID_HAIR:
|
||||
1. DiffuseColor = EvaluateEnvHair(GBuffer, V, N, L);
|
||||
2. SkyVisData.SkyDiffuseLookUpNormal = L;
|
||||
3. DiffuseWeight = 1.0f;
|
||||
4. 调用GetSkySHDiffuse()计算天光光照效果。GetSkySHDiffuse()本质是采样球谐贴图,来获得天光GI结果。
|
||||
|
||||
### ReflectionEnvironment
|
||||
```c++
|
||||
float3 ReflectionEnvironment(FGBufferData GBuffer, float AmbientOcclusion, float2 BufferUV, float2 ScreenPosition, float4 SvPosition, float3 BentNormal, float3 SpecularColor, uint ShadingModelID)
|
||||
{
|
||||
float4 Color = float4(0, 0, 0, 1);
|
||||
|
||||
float IndirectIrradiance = GBuffer.IndirectIrradiance;
|
||||
|
||||
#if ENABLE_SKY_LIGHT && ALLOW_STATIC_LIGHTING
|
||||
BRANCH
|
||||
// Add in diffuse contribution from dynamic skylights so reflection captures will have something to mix with
|
||||
if (ReflectionStruct.SkyLightParameters.y > 0 && ReflectionStruct.SkyLightParameters.z > 0)
|
||||
{
|
||||
//如果开启天光、并且开启静态关照。会在这里采样SkySH,以此累加间接照明。
|
||||
IndirectIrradiance += GetDynamicSkyIndirectIrradiance(BentNormal, GBuffer.WorldNormal);
|
||||
}
|
||||
#endif
|
||||
|
||||
//计算反射Vector、WorldNormal、ViewVector
|
||||
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, GBuffer.Depth), GBuffer.Depth, 1), View.ScreenToTranslatedWorld).xyz;
|
||||
float3 CameraToPixel = normalize(TranslatedWorldPosition - View.TranslatedWorldCameraOrigin);
|
||||
float3 ReflectionVector = reflect(CameraToPixel, GBuffer.WorldNormal);
|
||||
float3 V = -CameraToPixel;
|
||||
float3 N = GBuffer.WorldNormal;
|
||||
|
||||
const float3 SavedTopLayerNormal = N;
|
||||
|
||||
#if SUPPORTS_ANISOTROPIC_MATERIALS
|
||||
ModifyGGXAnisotropicNormalRoughness(GBuffer.WorldTangent, GBuffer.Anisotropy, GBuffer.Roughness, N, V);
|
||||
#endif
|
||||
|
||||
float3 R = 2 * dot( V, N ) * N - V;
|
||||
float NoV = saturate( dot( N, V ) );
|
||||
|
||||
// Point lobe in off-specular peak direction
|
||||
R = GetOffSpecularPeakReflectionDir(N, R, GBuffer.Roughness);
|
||||
|
||||
// 采样 SSR, planar reflections, RT reflections or Lumen 反射结果。
|
||||
float4 ReflectionInput = Texture2DSample(ReflectionTexture, ReflectionTextureSampler, BufferUV);
|
||||
Color = CompositeReflections(ReflectionInput, BufferUV, GBuffer.Roughness, ShadingModelID);//Color = float4(ReflectionInput.rgb, 1 - ReflectionInput.a)
|
||||
|
||||
#if RAY_TRACED_REFLECTIONS
|
||||
float4 SavedColor = Color; // When a clear coat material is encountered, we save the reflection buffer color for it to not be affected by operations.
|
||||
#endif
|
||||
if(GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT )
|
||||
{
|
||||
#if RAY_TRACED_REFLECTIONS
|
||||
Color = float4(0, 0, 0, 1); // Clear coat reflection is expected to be computed on a black background
|
||||
#endif
|
||||
const float ClearCoat = GBuffer.CustomData.x;
|
||||
Color = lerp( Color, float4(0,0,0,1), ClearCoat );
|
||||
|
||||
#if CLEAR_COAT_BOTTOM_NORMAL
|
||||
const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 4) - (512.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal);
|
||||
const float3 ClearCoatUnderNormal = OctahedronToUnitVector(oct1);
|
||||
|
||||
const float3 BottomEffectiveNormal = ClearCoatUnderNormal;
|
||||
R = 2 * dot( V, ClearCoatUnderNormal ) * ClearCoatUnderNormal - V;
|
||||
#endif
|
||||
}
|
||||
|
||||
float AO = GBuffer.GBufferAO * AmbientOcclusion;//AmbientOcclusion为SSAO或者RTAO或者DFAO或者Lumen……
|
||||
float RoughnessSq = GBuffer.Roughness * GBuffer.Roughness;
|
||||
float SpecularOcclusion = GetSpecularOcclusion(NoV, RoughnessSq, AO);
|
||||
Color.a *= SpecularOcclusion;
|
||||
|
||||
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
|
||||
float2 LocalPosition = SvPosition.xy - View.ViewRectMin.xy;
|
||||
|
||||
uint GridIndex = ComputeLightGridCellIndex(uint2(LocalPosition.x, LocalPosition.y), GBuffer.Depth);
|
||||
uint NumCulledEntryIndex = (ForwardLightData.NumGridCells + GridIndex) * NUM_CULLED_LIGHTS_GRID_STRIDE;
|
||||
uint NumCulledReflectionCaptures = min(ForwardLightData.NumCulledLightsGrid[NumCulledEntryIndex + 0], ForwardLightData.NumReflectionCaptures);
|
||||
uint DataStartIndex = ForwardLightData.NumCulledLightsGrid[NumCulledEntryIndex + 1];
|
||||
#else
|
||||
uint DataStartIndex = 0;
|
||||
uint NumCulledReflectionCaptures = 0;
|
||||
#endif
|
||||
|
||||
const FBxDFEnergyTerms EnergyTerms = ComputeGGXSpecEnergyTerms(GBuffer.Roughness, NoV, GBuffer.SpecularColor);
|
||||
|
||||
//常规反射 或 底层清漆 光照计算
|
||||
//Top of regular reflection or bottom layer of clear coat.
|
||||
Color.rgb += View.PreExposure * GatherRadiance(Color.a, TranslatedWorldPosition, R, GBuffer.Roughness, BentNormal, IndirectIrradiance, GBuffer.ShadingModelID, NumCulledReflectionCaptures, DataStartIndex);
|
||||
|
||||
BRANCH
|
||||
if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT)
|
||||
{
|
||||
const float ClearCoat = GBuffer.CustomData.x;
|
||||
const float ClearCoatRoughness = GBuffer.CustomData.y;
|
||||
|
||||
// Restore saved values needed for the top layer.
|
||||
GBuffer.WorldNormal = SavedTopLayerNormal;
|
||||
// Recompute some values unaffected by anistropy for the top layer
|
||||
N = GBuffer.WorldNormal;
|
||||
R = 2 * dot(V, N) * N - V;
|
||||
NoV = saturate(dot(N, V));
|
||||
R = GetOffSpecularPeakReflectionDir(N, R, ClearCoatRoughness);
|
||||
|
||||
// TODO EnvBRDF should have a mask param
|
||||
#if USE_ENERGY_CONSERVATION
|
||||
Color.rgb *= EnergyTerms.E * (1 - ClearCoat);
|
||||
#else
|
||||
// Hack: Ensures when clear coat is >0, grazing angle does not get too much energy,
|
||||
// but preserve response at normal incidence
|
||||
float2 AB = PreIntegratedGF.SampleLevel(PreIntegratedGFSampler, float2(NoV, GBuffer.Roughness), 0).rg;
|
||||
Color.rgb *= SpecularColor * AB.x + AB.y * saturate(50 * SpecularColor.g) * (1 - ClearCoat);
|
||||
#endif
|
||||
|
||||
// F_Schlick
|
||||
const float CoatF0 = 0.04f;
|
||||
#if USE_ENERGY_CONSERVATION
|
||||
float F = ComputeGGXSpecEnergyTerms(ClearCoatRoughness, NoV, CoatF0).E.x;
|
||||
#else
|
||||
float F = EnvBRDF(CoatF0, ClearCoatRoughness, NoV).x;
|
||||
#endif
|
||||
|
||||
F *= ClearCoat;
|
||||
|
||||
float LayerAttenuation = (1 - F);
|
||||
Color.rgb *= LayerAttenuation;
|
||||
Color.a = F;
|
||||
|
||||
#if !RAY_TRACED_REFLECTIONS
|
||||
Color.rgb += ReflectionInput.rgb * F;
|
||||
Color.a *= 1 - ReflectionInput.a;
|
||||
#endif
|
||||
|
||||
Color.a *= SpecularOcclusion;
|
||||
|
||||
float3 TopLayerR = 2 * dot( V, N ) * N - V;
|
||||
Color.rgb += View.PreExposure * GatherRadiance(Color.a, TranslatedWorldPosition, TopLayerR, ClearCoatRoughness, BentNormal, IndirectIrradiance, GBuffer.ShadingModelID, NumCulledReflectionCaptures, DataStartIndex);
|
||||
|
||||
#if RAY_TRACED_REFLECTIONS
|
||||
Color.rgb = SavedColor.rgb + Color.rgb * SavedColor.a; // Compose default clear coat reflection over regular refelction (using Premultiplied alpha where SaveColor.a=transmittance)
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if USE_ENERGY_CONSERVATION
|
||||
Color.rgb *= EnergyTerms.E;
|
||||
#else
|
||||
Color.rgb *= EnvBRDF( SpecularColor, GBuffer.Roughness, NoV );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Transform NaNs to black, transform negative colors to black.
|
||||
return -min(-Color.rgb, 0.0);
|
||||
}
|
||||
```
|
||||
|
||||
### GatherRadiance()
|
||||
GatherRadiance()主要计算了SkyLightTexture(天空盒贴图)以及ReflectionCapture(Box、Sphere反射球),最终根据之前计算的Color.a进行合成。
|
||||
```c++
|
||||
float3 GatherRadiance(float CompositeAlpha, float3 TranslatedWorldPosition, float3 RayDirection, float Roughness, float3 BentNormal, float IndirectIrradiance, uint ShadingModelID, uint NumCulledReflectionCaptures, uint CaptureDataStartIndex)
|
||||
{
|
||||
// Indirect occlusion from DFAO, which should be applied to reflection captures and skylight specular, but not SSR
|
||||
float IndirectSpecularOcclusion = 1.0f;
|
||||
float3 ExtraIndirectSpecular = 0;
|
||||
|
||||
#if SUPPORT_DFAO_INDIRECT_OCCLUSION
|
||||
float IndirectDiffuseOcclusion;
|
||||
GetDistanceFieldAOSpecularOcclusion(BentNormal, RayDirection, Roughness, ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE, IndirectSpecularOcclusion, IndirectDiffuseOcclusion, ExtraIndirectSpecular);
|
||||
// Apply DFAO to IndirectIrradiance before mixing with indirect specular
|
||||
IndirectIrradiance *= IndirectDiffuseOcclusion;
|
||||
#endif
|
||||
|
||||
const bool bCompositeSkylight = true;
|
||||
return CompositeReflectionCapturesAndSkylightTWS(
|
||||
CompositeAlpha,
|
||||
TranslatedWorldPosition,
|
||||
RayDirection,
|
||||
Roughness,
|
||||
IndirectIrradiance,
|
||||
IndirectSpecularOcclusion,
|
||||
ExtraIndirectSpecular,
|
||||
NumCulledReflectionCaptures,
|
||||
CaptureDataStartIndex,
|
||||
0,
|
||||
bCompositeSkylight);
|
||||
}
|
||||
```
|
||||
|
||||
主要逻辑位于CompositeReflectionCapturesAndSkylightTWS():
|
||||
```c++
|
||||
float3 CompositeReflectionCapturesAndSkylightTWS(
|
||||
float CompositeAlpha,
|
||||
float3 TranslatedWorldPosition,
|
||||
float3 RayDirection,
|
||||
float Roughness,
|
||||
float IndirectIrradiance,
|
||||
float IndirectSpecularOcclusion,
|
||||
float3 ExtraIndirectSpecular,
|
||||
uint NumCapturesAffectingTile,
|
||||
uint CaptureDataStartIndex,
|
||||
int SingleCaptureIndex,
|
||||
bool bCompositeSkylight,
|
||||
uint EyeIndex)
|
||||
{
|
||||
float Mip = ComputeReflectionCaptureMipFromRoughness(Roughness, View.ReflectionCubemapMaxMip);
|
||||
float4 ImageBasedReflections = float4(0, 0, 0, CompositeAlpha);
|
||||
float2 CompositedAverageBrightness = float2(0.0f, 1.0f);
|
||||
|
||||
#if REFLECTION_COMPOSITE_USE_BLENDED_REFLECTION_CAPTURES
|
||||
// Accumulate reflections from captures affecting this tile, applying largest captures first so that the smallest ones display on top
|
||||
//ReflectionCapture Blend,其顺序为大范围在前
|
||||
LOOP
|
||||
for (uint TileCaptureIndex = 0; TileCaptureIndex < NumCapturesAffectingTile; TileCaptureIndex++)
|
||||
{
|
||||
BRANCH
|
||||
if (ImageBasedReflections.a < 0.001)//如果Alpha小于0.001则停止循环,结束合成计算。
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//计算CaptureIndex
|
||||
uint CaptureIndex = 0;
|
||||
#ifdef REFLECTION_COMPOSITE_NO_CULLING_DATA
|
||||
CaptureIndex = TileCaptureIndex; // Go from 0 to NumCapturesAffectingTile as absolute index in capture array
|
||||
#else
|
||||
#if (INSTANCED_STEREO || MOBILE_MULTI_VIEW)//VR或者移动端多View
|
||||
BRANCH
|
||||
if (EyeIndex == 0)
|
||||
{
|
||||
#endif
|
||||
|
||||
CaptureIndex = GetCulledLightDataGrid(CaptureDataStartIndex + TileCaptureIndex);
|
||||
|
||||
#if (INSTANCED_STEREO || MOBILE_MULTI_VIEW)
|
||||
}
|
||||
else
|
||||
{
|
||||
CaptureIndex = GetCulledLightDataGridISR(CaptureDataStartIndex + TileCaptureIndex);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FLWCVector3 CaptureWorldPosition = MakeLWCVector3(GetReflectionTilePosition(CaptureIndex).xyz, GetReflectionPositionAndRadius(CaptureIndex).xyz);
|
||||
float3 CaptureTranslatedWorldPosition = LWCToFloat(LWCAdd(CaptureWorldPosition, ResolvedView.PreViewTranslation));
|
||||
float CaptureRadius = GetReflectionPositionAndRadius(CaptureIndex).w;
|
||||
|
||||
float4 CaptureProperties = GetReflectionCaptureProperties(CaptureIndex);
|
||||
|
||||
float3 CaptureVector = TranslatedWorldPosition - CaptureTranslatedWorldPosition;
|
||||
float CaptureVectorLength = sqrt(dot(CaptureVector, CaptureVector));
|
||||
float NormalizedDistanceToCapture = saturate(CaptureVectorLength / CaptureRadius);
|
||||
|
||||
BRANCH
|
||||
if (CaptureVectorLength < CaptureRadius)//当前像素处于ReflectionCapture范围内
|
||||
{
|
||||
float3 ProjectedCaptureVector = RayDirection;
|
||||
float4 CaptureOffsetAndAverageBrightness = GetReflectionCaptureOffsetAndAverageBrightness(CaptureIndex);
|
||||
|
||||
// Fade out based on distance to capture
|
||||
float DistanceAlpha = 0;
|
||||
|
||||
#define PROJECT_ONTO_SHAPE 1
|
||||
#if PROJECT_ONTO_SHAPE
|
||||
//盒形反射球
|
||||
#if REFLECTION_COMPOSITE_HAS_BOX_CAPTURES
|
||||
#if REFLECTION_COMPOSITE_HAS_SPHERE_CAPTURES
|
||||
// Box
|
||||
BRANCH if (CaptureProperties.b > 0)
|
||||
#endif
|
||||
{
|
||||
ProjectedCaptureVector = GetLookupVectorForBoxCapture(RayDirection, TranslatedWorldPosition, float4(CaptureTranslatedWorldPosition, CaptureRadius),
|
||||
GetReflectionBoxTransform(CaptureIndex), GetReflectionBoxScales(CaptureIndex), CaptureOffsetAndAverageBrightness.xyz, DistanceAlpha);
|
||||
}
|
||||
#endif
|
||||
|
||||
//球形反射球
|
||||
#if REFLECTION_COMPOSITE_HAS_SPHERE_CAPTURES
|
||||
// Sphere
|
||||
#if REFLECTION_COMPOSITE_HAS_BOX_CAPTURES
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ProjectedCaptureVector = GetLookupVectorForSphereCapture(RayDirection, TranslatedWorldPosition, float4(CaptureTranslatedWorldPosition, CaptureRadius), NormalizedDistanceToCapture, CaptureOffsetAndAverageBrightness.xyz, DistanceAlpha);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
DistanceAlpha = 1.0;
|
||||
#endif //PROJECT_ONTO_SHAPE
|
||||
|
||||
float CaptureArrayIndex = CaptureProperties.g;
|
||||
|
||||
{
|
||||
//采样对应的ReflectionCubeMap,Sample.a为根据距离计算的Alpha。最后结果累加到ImageBasedReflections中。
|
||||
float4 Sample = ReflectionStruct.ReflectionCubemap.SampleLevel(ReflectionStruct.ReflectionCubemapSampler, float4(ProjectedCaptureVector, CaptureArrayIndex), Mip);
|
||||
|
||||
Sample.rgb *= CaptureProperties.r;
|
||||
Sample *= DistanceAlpha;
|
||||
|
||||
// Under operator (back to front)
|
||||
ImageBasedReflections.rgb += Sample.rgb * ImageBasedReflections.a * IndirectSpecularOcclusion;
|
||||
ImageBasedReflections.a *= 1 - Sample.a;
|
||||
|
||||
float AverageBrightness = CaptureOffsetAndAverageBrightness.w;
|
||||
CompositedAverageBrightness.x += AverageBrightness * DistanceAlpha * CompositedAverageBrightness.y;
|
||||
CompositedAverageBrightness.y *= 1 - DistanceAlpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
//非ReflectionCapture Blend
|
||||
float3 ProjectedCaptureVector = RayDirection;
|
||||
|
||||
FLWCVector3 SingleCaptureWorldPosition = MakeLWCVector3(GetReflectionTilePosition(SingleCaptureIndex).xyz, GetReflectionPositionAndRadius(SingleCaptureIndex).xyz);
|
||||
float3 SingleCaptureTranslatedWorldPosition = LWCToFloat(LWCAdd(SingleCaptureWorldPosition, ResolvedView.PreViewTranslation));
|
||||
float SingleCaptureRadius = GetReflectionPositionAndRadius(SingleCaptureIndex).w;
|
||||
|
||||
float4 SingleCaptureOffsetAndAverageBrightness = GetReflectionCaptureOffsetAndAverageBrightness(SingleCaptureIndex);
|
||||
float SingleCaptureBrightness = GetReflectionCaptureProperties(SingleCaptureIndex).x;
|
||||
float SingleCaptureArrayIndex = GetReflectionCaptureProperties(SingleCaptureIndex).y;
|
||||
|
||||
#define APPROXIMATE_CONTINUOUS_SINGLE_CAPTURE_PARALLAX 0
|
||||
#if APPROXIMATE_CONTINUOUS_SINGLE_CAPTURE_PARALLAX
|
||||
float3 CaptureVector = TranslatedWorldPosition - SingleCaptureTranslatedWorldPosition;
|
||||
float CaptureVectorLength = sqrt(dot(CaptureVector, CaptureVector));
|
||||
float NormalizedDistanceToCapture = saturate(CaptureVectorLength / SingleCaptureRadius);
|
||||
|
||||
float UnusedDistanceAlpha = 0;
|
||||
ProjectedCaptureVector = GetLookupVectorForSphereCapture(RayDirection, TranslatedWorldPosition, float4(SingleCaptureTranslatedWorldPosition, SingleCaptureRadius), NormalizedDistanceToCapture, SingleCaptureOffsetAndAverageBrightness.xyz, UnusedDistanceAlpha);
|
||||
|
||||
float x = saturate(NormalizedDistanceToCapture);
|
||||
float DistanceAlpha = 1 - x * x * (3 - 2 * x);
|
||||
// Lerp between sphere parallax corrected and infinite based on distance to shape
|
||||
ProjectedCaptureVector = lerp(RayDirection, normalize(ProjectedCaptureVector), DistanceAlpha);
|
||||
#endif
|
||||
|
||||
float4 Sample = TextureCubeArraySampleLevel(ReflectionStruct.ReflectionCubemap, ReflectionStruct.ReflectionCubemapSampler, ProjectedCaptureVector, SingleCaptureArrayIndex, Mip);
|
||||
|
||||
Sample.rgb *= SingleCaptureBrightness;
|
||||
ImageBasedReflections = float4(Sample.rgb, 1 - Sample.a);
|
||||
|
||||
float AverageBrightness = SingleCaptureOffsetAndAverageBrightness.w;
|
||||
CompositedAverageBrightness.x += AverageBrightness * CompositedAverageBrightness.y;
|
||||
CompositedAverageBrightness.y = 0;
|
||||
#endif
|
||||
|
||||
// Apply indirect lighting scale while we have only accumulated reflection captures
|
||||
ImageBasedReflections.rgb *= View.PrecomputedIndirectSpecularColorScale;
|
||||
CompositedAverageBrightness.x *= Luminance( View.PrecomputedIndirectSpecularColorScale );
|
||||
|
||||
#if ENABLE_SKY_LIGHT
|
||||
|
||||
BRANCH
|
||||
if (ReflectionStruct.SkyLightParameters.y > 0 && bCompositeSkylight)
|
||||
{
|
||||
float SkyAverageBrightness = 1.0f;
|
||||
|
||||
// 不支持Blend的,结果大致为SkyLightCubeMap * View.SkyLightColor。支持Blend的,lerp(Reflection, BlendDestinationReflection * View.SkyLightColor.rgb, ReflectionStruct.SkyLightParameters.w);
|
||||
#if REFLECTION_COMPOSITE_SUPPORT_SKYLIGHT_BLEND
|
||||
float3 SkyLighting = GetSkyLightReflectionSupportingBlend(RayDirection, Roughness, SkyAverageBrightness);
|
||||
#else
|
||||
float3 SkyLighting = GetSkyLightReflection(RayDirection, Roughness, SkyAverageBrightness);
|
||||
#endif
|
||||
|
||||
// Normalize for static skylight types which mix with lightmaps, material ambient occlusion as well as diffuse/specular occlusion.
|
||||
bool bNormalize = ReflectionStruct.SkyLightParameters.z < 1 && ALLOW_STATIC_LIGHTING;
|
||||
|
||||
FLATTEN
|
||||
if (bNormalize)
|
||||
{
|
||||
ImageBasedReflections.rgb += ImageBasedReflections.a * SkyLighting * IndirectSpecularOcclusion;
|
||||
CompositedAverageBrightness.x += SkyAverageBrightness * CompositedAverageBrightness.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtraIndirectSpecular += SkyLighting * IndirectSpecularOcclusion;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ALLOW_STATIC_LIGHTING
|
||||
ImageBasedReflections.rgb *= ComputeMixingWeight(IndirectIrradiance, CompositedAverageBrightness.x, Roughness);
|
||||
#endif
|
||||
|
||||
ImageBasedReflections.rgb += ImageBasedReflections.a * ExtraIndirectSpecular;
|
||||
|
||||
return ImageBasedReflections.rgb;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## DiffuseIndirectComposite
|
||||
位于IndirectLightRendering.cpp的`RenderDiffuseIndirectAndAmbientOcclusion()`
|
||||
|
||||
`RenderDiffuseIndirectAndAmbientOcclusion()`主要执行了:
|
||||
1. 进行判断是否跳过当前View的计算。(主要是判断是否开启Lumen,以及传入的bCompositeRegularLumenOnly变量)
|
||||
2. SetupCommonDiffuseIndirectParameters()
|
||||
3. 计算GI
|
||||
- ScreenSpaceGI
|
||||
- RTGI
|
||||
- Lumen
|
||||
- Lumen Reflection:输出结果到OutTextures.Textures[3]。
|
||||
- SSR:输出结果到OutTextures.Textures[3]。
|
||||
- 其他Reflection:结果输出为黑色。
|
||||
4. 使用降噪器对SSGI与RTGI进行降噪
|
||||
5. 渲染AO
|
||||
- 禁用
|
||||
- RTAO
|
||||
- SSAO
|
||||
- 其他没有实现的会谈报错提醒
|
||||
6. 将渲染的AO结果赋予SceneTextures.ScreenSpaceAO
|
||||
7. RenderHairStrandsAmbientOcclusion()
|
||||
8. 应用GI到渲染结果上。
|
||||
9. ApplyAmbientCubemapComposite()
|
||||
|
||||
FDiffuseIndirectCompositePS位于/Engine/Private/DiffuseIndirectComposite.usf的MainPS()
|
||||
|
||||
### FDiffuseIndirectCompositePS
|
||||
```c++
|
||||
void MainPS(
|
||||
float4 SvPosition : SV_POSITION
|
||||
#if DIM_APPLY_DIFFUSE_INDIRECT
|
||||
#if ENABLE_DUAL_SRC_BLENDING
|
||||
, out float4 OutAddColor DUAL_SOURCE_BLENDING_SLOT(0) : SV_Target0
|
||||
, out float4 OutMultiplyColor DUAL_SOURCE_BLENDING_SLOT(1) : SV_Target1
|
||||
#else
|
||||
, out float4 OutColor : SV_Target0
|
||||
#endif
|
||||
#else
|
||||
, out float4 OutMultiplyColor : SV_Target0
|
||||
#endif
|
||||
)
|
||||
{
|
||||
const uint2 PixelPos = SvPosition.xy;
|
||||
const float2 SceneBufferUV = SvPositionToBufferUV(SvPosition);
|
||||
const float2 ScreenPosition = SvPositionToScreenPosition(SvPosition).xy;
|
||||
|
||||
#if !ENABLE_DUAL_SRC_BLENDING && DIM_APPLY_DIFFUSE_INDIRECT
|
||||
float4 OutAddColor = float4(0.0, 0.0, 0.0, 0.0);
|
||||
float4 OutMultiplyColor;
|
||||
|
||||
const float4 SceneColor = SceneColorTexture.SampleLevel(SceneColorSampler, SceneBufferUV, 0);
|
||||
#endif
|
||||
|
||||
// Sample scene textures.
|
||||
const FLumenMaterialData Material = ReadMaterialData(PixelPos, SceneBufferUV);
|
||||
|
||||
// Sample the ambient occlusion that is dynamically generated every frame.
|
||||
const float DynamicAmbientOcclusion = AmbientOcclusionTexture.SampleLevel(AmbientOcclusionSampler, SceneBufferUV, 0).r;
|
||||
|
||||
// Compute the final ambient occlusion to be applied. Lumen handles material AO internally.
|
||||
float FinalAmbientOcclusion = 1.0f;
|
||||
#if DIM_APPLY_DIFFUSE_INDIRECT != DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
|
||||
{
|
||||
float AOMask = IsValid(Material);
|
||||
FinalAmbientOcclusion = lerp(1.0f, Material.MaterialAO * DynamicAmbientOcclusion, AOMask * AmbientOcclusionStaticFraction);
|
||||
}
|
||||
#endif
|
||||
|
||||
const float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, Material.SceneDepth), Material.SceneDepth, 1), View.ScreenToTranslatedWorld).xyz;
|
||||
|
||||
const float3 N = Material.WorldNormal;
|
||||
const float3 V = normalize(View.TranslatedWorldCameraOrigin - TranslatedWorldPosition);
|
||||
const float NoV = saturate(dot(N, V));
|
||||
|
||||
// Apply diffuse indirect.
|
||||
//这里的DIM可能是Diffuse Indirect Map的缩写
|
||||
#if DIM_APPLY_DIFFUSE_INDIRECT
|
||||
OutAddColor = 0;
|
||||
{
|
||||
FDirectLighting IndirectLighting = (FDirectLighting)0;
|
||||
|
||||
if (IsValid(Material))
|
||||
{
|
||||
float3 DiffuseIndirectLighting = 0;
|
||||
float3 RoughSpecularIndirectLighting = 0;
|
||||
float4 SpecularIndirectLighting = 0;
|
||||
|
||||
//直接从屏幕空间探针中获取数据(Lumen)
|
||||
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
|
||||
DiffuseIndirectLighting = DiffuseIndirect_Textures_0.SampleLevel(GlobalPointClampedSampler, SceneBufferUV, 0).rgb;
|
||||
RoughSpecularIndirectLighting = DiffuseIndirect_Textures_2.SampleLevel(GlobalPointClampedSampler, SceneBufferUV, 0).rgb;
|
||||
SpecularIndirectLighting = DiffuseIndirect_Textures_3.SampleLevel(GlobalPointClampedSampler, SceneBufferUV, 0).rgba;
|
||||
#else
|
||||
{
|
||||
//设置降噪器参数
|
||||
// Sample the output of the denoiser.
|
||||
FSSDKernelConfig KernelConfig = CreateKernelConfig();
|
||||
|
||||
#if DEBUG_OUTPUT
|
||||
{
|
||||
KernelConfig.DebugPixelPosition = uint2(SvPosition.xy);
|
||||
KernelConfig.DebugEventCounter = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compile time.
|
||||
KernelConfig.bSampleKernelCenter = true;
|
||||
KernelConfig.BufferLayout = CONFIG_SIGNAL_INPUT_LAYOUT;
|
||||
KernelConfig.bUnroll = true;
|
||||
|
||||
#if DIM_UPSCALE_DIFFUSE_INDIRECT
|
||||
{
|
||||
KernelConfig.SampleSet = SAMPLE_SET_2X2_BILINEAR;
|
||||
KernelConfig.BilateralDistanceComputation = SIGNAL_WORLD_FREQUENCY_REF_METADATA_ONLY;
|
||||
KernelConfig.WorldBluringDistanceMultiplier = 16.0;
|
||||
|
||||
KernelConfig.BilateralSettings[0] = BILATERAL_POSITION_BASED(3);
|
||||
|
||||
// SGPRs
|
||||
KernelConfig.BufferSizeAndInvSize = View.BufferSizeAndInvSize * float4(0.5, 0.5, 2.0, 2.0);
|
||||
KernelConfig.BufferBilinearUVMinMax = View.BufferBilinearUVMinMax;
|
||||
}
|
||||
#else
|
||||
{
|
||||
KernelConfig.SampleSet = SAMPLE_SET_1X1;
|
||||
KernelConfig.bNormalizeSample = true;
|
||||
|
||||
// SGPRs
|
||||
KernelConfig.BufferSizeAndInvSize = View.BufferSizeAndInvSize;
|
||||
KernelConfig.BufferBilinearUVMinMax = View.BufferBilinearUVMinMax;
|
||||
}
|
||||
#endif
|
||||
|
||||
// VGPRs
|
||||
KernelConfig.BufferUV = SceneBufferUV;
|
||||
{
|
||||
// STRATA_TODO: We use the top layer data, but we should resolve lighting for each BSDFs.
|
||||
KernelConfig.CompressedRefSceneMetadata = MaterialToCompressedSceneMetadata(Material.SceneDepth, Material.WorldNormal, Material.Roughness, Material.ShadingID);
|
||||
KernelConfig.RefBufferUV = SceneBufferUV;
|
||||
KernelConfig.RefSceneMetadataLayout = METADATA_BUFFER_LAYOUT_DISABLED;
|
||||
}
|
||||
KernelConfig.HammersleySeed = Rand3DPCG16(int3(SvPosition.xy, View.StateFrameIndexMod8)).xy;
|
||||
|
||||
FSSDSignalAccumulatorArray UncompressedAccumulators = CreateSignalAccumulatorArray();
|
||||
FSSDCompressedSignalAccumulatorArray CompressedAccumulators = CompressAccumulatorArray(
|
||||
UncompressedAccumulators, CONFIG_ACCUMULATOR_VGPR_COMPRESSION);
|
||||
|
||||
AccumulateKernel(
|
||||
KernelConfig,
|
||||
DiffuseIndirect_Textures_0,
|
||||
DiffuseIndirect_Textures_1,
|
||||
DiffuseIndirect_Textures_2,
|
||||
DiffuseIndirect_Textures_3,
|
||||
/* inout */ UncompressedAccumulators,
|
||||
/* inout */ CompressedAccumulators);
|
||||
|
||||
//PassDebugOutput[uint2(SvPosition.xy)] = float4(UncompressedAccumulators.Array[0].Moment1.SampleCount, 0, 0, 0);
|
||||
|
||||
FSSDSignalSample Sample;
|
||||
#if DIM_UPSCALE_DIFFUSE_INDIRECT
|
||||
Sample = NormalizeToOneSample(UncompressedAccumulators.Array[0].Moment1);
|
||||
#else
|
||||
Sample = UncompressedAccumulators.Array[0].Moment1;
|
||||
#endif
|
||||
|
||||
//SSGI、RTGI直接将降噪(上采样)结果赋予DiffuseIndirectLighting
|
||||
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SSGI || DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_RTGI
|
||||
{
|
||||
DiffuseIndirectLighting = Sample.SceneColor.rgb;
|
||||
}
|
||||
#else
|
||||
#error Unimplemented
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STRATA_ENABLED
|
||||
{
|
||||
FStrataAddressing StrataAddressing = GetStrataPixelDataByteOffset(PixelPos, uint2(View.BufferSizeAndInvSize.xy), Strata.MaxBytesPerPixel);
|
||||
FStrataPixelHeader StrataPixelHeader = UnpackStrataHeaderIn(Strata.MaterialTextureArray, StrataAddressing, Strata.TopLayerTexture);
|
||||
if (StrataPixelHeader.GetMaterialMode() > HEADER_MATERIALMODE_NONE)
|
||||
{
|
||||
const FStrataDeferredLighting IndirectLighting_Strata = StrataIndirectLighting(
|
||||
PixelPos,
|
||||
Strata.MaterialTextureArray,
|
||||
StrataAddressing,
|
||||
StrataPixelHeader,
|
||||
V,
|
||||
DynamicAmbientOcclusion,
|
||||
DiffuseIndirectLighting,
|
||||
SpecularIndirectLighting);
|
||||
|
||||
OutAddColor = IndirectLighting_Strata.SceneColor;
|
||||
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
|
||||
const uint2 OutCoord = PixelPos;
|
||||
OutOpaqueRoughRefractionSceneColor[OutCoord] = IndirectLighting_Strata.OpaqueRoughRefractionSceneColor;
|
||||
OutSubSurfaceSceneColor[OutCoord] = IndirectLighting_Strata.SubSurfaceSceneColor;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else // STRATA_ENABLED
|
||||
{
|
||||
FGBufferData GBuffer = Material.GBufferData;
|
||||
|
||||
float3 DiffuseColor = bVisualizeDiffuseIndirect ? float3(.18f, .18f, .18f) : GBuffer.DiffuseColor;
|
||||
float3 SpecularColor = GBuffer.SpecularColor;
|
||||
|
||||
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
|
||||
RemapClearCoatDiffuseAndSpecularColor(GBuffer, NoV, DiffuseColor, SpecularColor);
|
||||
#endif
|
||||
|
||||
//取得AO
|
||||
FShadingOcclusion Occlusion = GetShadingOcclusion(PixelPos, V, N, GBuffer.Roughness, GBuffer.BaseColor, DynamicAmbientOcclusion);
|
||||
|
||||
//对双面植被与次表面进行DiffuseOcclusion适配
|
||||
if (GBuffer.ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE || GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE)
|
||||
{
|
||||
Occlusion.DiffuseOcclusion = lerp(1, Occlusion.DiffuseOcclusion, LumenFoliageOcclusionStrength);
|
||||
}
|
||||
|
||||
//Hair需要单独处理
|
||||
if (GBuffer.ShadingModelID == SHADINGMODELID_HAIR)
|
||||
{
|
||||
float3 L = 0;
|
||||
const float3 IndirectDiffuseColor = EvaluateEnvHair(GBuffer, V, N, L /*out*/);
|
||||
IndirectLighting.Diffuse = DiffuseIndirectLighting * Occlusion.DiffuseOcclusion * IndirectDiffuseColor;
|
||||
IndirectLighting.Specular = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float3 BackfaceDiffuseIndirectLighting = 0;
|
||||
|
||||
//双面植被,计算BackfaceDiffuseIndirectLighting。如果不支持BackfaceDiffuse则将预积分结果加到DiffuseColor中。
|
||||
if (GBuffer.ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE)
|
||||
{
|
||||
float3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
|
||||
|
||||
if (bLumenSupportBackfaceDiffuse > 0)
|
||||
{
|
||||
BackfaceDiffuseIndirectLighting += SubsurfaceColor * DiffuseIndirect_Textures_1.SampleLevel(GlobalPointClampedSampler, SceneBufferUV, 0).rgb;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding Subsurface energy to the diffuse lobe is a poor approximation when DiffuseColor is small and SubsurfaceColor is large
|
||||
// Reduce the error by attenuating SubsurfaceColor, even though diffuse already has the 1/PI for Lambert.
|
||||
const float PreintegratedTwoSidedBxDF = 1.0f / PI;
|
||||
DiffuseColor += SubsurfaceColor * PreintegratedTwoSidedBxDF;
|
||||
}
|
||||
}
|
||||
|
||||
//次表面、预积分皮肤,直接将次表面颜色加到Diffsue上。
|
||||
if (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE || GBuffer.ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN)
|
||||
{
|
||||
float3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
|
||||
// Add subsurface energy to diffuse
|
||||
DiffuseColor += SubsurfaceColor;
|
||||
}
|
||||
|
||||
//布料计算
|
||||
if (GBuffer.ShadingModelID == SHADINGMODELID_CLOTH)
|
||||
{
|
||||
float3 ClothFuzz = ExtractSubsurfaceColor(GBuffer);
|
||||
DiffuseColor += ClothFuzz * GBuffer.CustomData.a;
|
||||
}
|
||||
|
||||
//最终GI结果计算
|
||||
IndirectLighting.Diffuse = (DiffuseIndirectLighting * DiffuseColor + BackfaceDiffuseIndirectLighting) * Occlusion.DiffuseOcclusion;
|
||||
IndirectLighting.Transmission = 0;
|
||||
|
||||
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
|
||||
//Lumen计算的反射
|
||||
RoughSpecularIndirectLighting *= Occlusion.SpecularOcclusion;
|
||||
IndirectLighting.Specular = CombineRoughSpecular(GBuffer, HasBackfaceDiffuse(Material), NoV, SpecularIndirectLighting, RoughSpecularIndirectLighting, SpecularColor);
|
||||
#else
|
||||
//SSGI、RTGI等其他GI方法的反射结果
|
||||
IndirectLighting.Specular = AddContrastAndSpecularScale(SpecularIndirectLighting.xyz) * EnvBRDF(SpecularColor, GBuffer.Roughness, NoV);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // STRATA_ENABLED
|
||||
}
|
||||
|
||||
// Accumulate lighting into the final buffers
|
||||
#if !STRATA_ENABLED
|
||||
IndirectLighting.Specular *= GetSSSCheckerboadSpecularScale(PixelPos, Material.bNeedsSeparateLightAccumulation);
|
||||
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
|
||||
LightAccumulator_Add(
|
||||
LightAccumulator,
|
||||
IndirectLighting.Diffuse + IndirectLighting.Specular,
|
||||
IndirectLighting.Diffuse,
|
||||
1.0f,
|
||||
Material.bNeedsSeparateLightAccumulation);
|
||||
OutAddColor = LightAccumulator_GetResult(LightAccumulator);
|
||||
#endif // !STRATA_ENABLED
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
OutMultiplyColor = FinalAmbientOcclusion;
|
||||
|
||||
#if !ENABLE_DUAL_SRC_BLENDING && DIM_APPLY_DIFFUSE_INDIRECT
|
||||
OutColor = SceneColor * OutMultiplyColor + OutAddColor;
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
PS.
|
||||
1. 采样自DiffuseIndirect_Textures_2的RoughSpecularIndirectLighting只会用在Lumen的计算中。
|
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海1.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海2.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海3.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海3.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海4.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海4.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海_皮肤.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_心海_皮肤.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_甘雨_皮肤.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_甘雨_皮肤.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_雷电将军_皮肤.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/原神_雷电将军_皮肤.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/地错_皮肤.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/地错_皮肤.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/碧蓝幻想_奶刀_皮肤.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/AI图参考/碧蓝幻想_奶刀_皮肤.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222449.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222449.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222502.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222502.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222506.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222506.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222509.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222509.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222512.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222512.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222514.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222514.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222517.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222517.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222520.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222520.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222523.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222523.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222526.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222526.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222528.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222528.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222531.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222531.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222533.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222533.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222536.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222536.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222538.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222538.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222541.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222541.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222545.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222545.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222547.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222547.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222550.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222550.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222553.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222553.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222555.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222555.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222557.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222557.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222559.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222559.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222603.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222603.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222606.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222606.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222608.jpg
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/丝袜参考/QQ图片20240605222608.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ColinLeung-NiloCat.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ColinLeung-NiloCat.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MMD_原神_仆人.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MMD_原神_仆人.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_与实拍视频结合.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_与实拍视频结合.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_与绘画背景结合.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_与绘画背景结合.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_多版本比较.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_多版本比较.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_天空盒傍晚.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_天空盒傍晚.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_室内上午.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_室内上午.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_室内傍晚.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_室内傍晚.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_室外上午.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_室外上午.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_室外夜晚.png
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/MuRo_光影参考_室外夜晚.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2557aa6acc90d03f2974e3126b138e733dd377165c3142be65ca9e445a146704
|
||||
size 3465588
|
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2865.JPG
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2865.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2866.JPG
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2866.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2867.JPG
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2867.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2868.JPG
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2868.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2869.JPG
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2869.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2870.JPG
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2870.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2871.JPG
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2871.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2872.JPG
(Stored with Git LFS)
Normal file
BIN
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/ピロ水/IMG_2872.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user