深入URP之Shader篇8: SimpleLit Shader分析(4)
Simple Lit Forward Pass
本篇继续 Fragment shader 函数
InitializeInputData
InputData inputData;
InitializeInputData(input, normalTS, inputData);
InputData
是URP ShaderLibrary的Input.hlsl
中定义的一个结构体:
struct InputData
{float3 positionWS;half3 normalWS;half3 viewDirectionWS;float4 shadowCoord;half fogCoord;half3 vertexLighting;half3 bakedGI;float2 normalizedScreenSpaceUV;half4 shadowMask;
};
这个结构体包含了光照计算所需要的所有输入参数,有些参数是直接从Varying获取,有些是进一步计算得到。这个结构体大量用于URP的各个Shader中。而InitializeInputData
的作用就是设置这个结构体的各个成员值,这个函数名也是URP的一个惯例,很多shader中都会有这个函数,当然函数参数可能不一样,但是作用是一样的。我们看下SimpleLit Shader的这个函数吧。
void InitializeInputData(Varyings input, half3 normalTS, out InputData inputData)
{inputData.positionWS = input.posWS;#ifdef _NORMALMAPhalf3 viewDirWS = half3(input.normal.w, input.tangent.w, input.bitangent.w);inputData.normalWS = TransformTangentToWorld(normalTS,half3x3(input.tangent.xyz, input.bitangent.xyz, input.normal.xyz));
#elsehalf3 viewDirWS = input.viewDir;inputData.normalWS = input.normal;
#endifinputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS);viewDirWS = SafeNormalize(viewDirWS);inputData.viewDirectionWS = viewDirWS;#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)inputData.shadowCoord = input.shadowCoord;
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS);
#elseinputData.shadowCoord = float4(0, 0, 0, 0);
#endifinputData.fogCoord = input.fogFactorAndVertexLight.x;inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);inputData.shadowMask = SAMPLE_SHADOWMASK(input.lightmapUV);
}
positionWS
直接从Varyings input得到viewDirWS
和normalWS
则是根据是否使用法线贴图分别设置。如果使用了法线贴图,回忆一下Vertex Shader中输出到Varying的操作,viewDirWS
是存放在NTB
三个向量的w中的。而normalWS
是将法线贴图中存放的切线空间法线,也就是传入的normalTS
,从切线空间变换到世界空间得到。重点来了,这儿使用的矩阵3x3矩阵是half3x3(input.tangent.xyz, input.bitangent.xyz, input.normal.xyz)
, 这样构造出来的矩阵的3个行分别是TBN三个向量,而关于空间变换矩阵有一个公共性质就是矩阵中包含了变换后的坐标轴,比如在切线空间中,三个坐标轴TBN分别是x,y,z轴单位向量,通过切线空间到世界空间的变换矩阵会分别变换成世界空间的TBN,也就是我们这儿的3个参数,那么以这三个世界空间向量组成的矩阵就是从切线空间变换到世界空间的矩阵。这儿3个向量是按行填入矩阵的,所以矩阵的3行就分别是3个轴,这样的矩阵如果想让任意向量从切线空间变换到世界空间,需要使用行向量在左乘矩阵的方式,这也正是TransformTangentToWorld
的做法:
real3 TransformTangentToWorld(real3 dirTS, real3x3 tangentToWorld)
{// Note matrix is in row major convention with left multiplication as it is build on the flyreturn mul(dirTS, tangentToWorld);
}
当然构建矩阵的时候也可以构建为列主的矩阵,但是这样比较麻烦,没有直接填充向量这么方便。
- 计算好
viewDirWS
和normalWS
后还要分别归一化,但是用了两个不同的函数,有什么区别呢?
normalWS
使用的是NormalizeNormalPerPixel
方法:
real3 NormalizeNormalPerPixel(real3 normalWS)
{#if defined(SHADER_QUALITY_HIGH) || defined(_NORMALMAP)return normalize(normalWS);#elsereturn normalWS;#endif
}
根据是否使用高质量shader,或者是否使用了法线贴图,来决定是否执行归一化法线。
viewDirWS
使用的是SafeNormalize
方法,这是SRP Core中的:
// Normalize that account for vectors with zero length
real3 SafeNormalize(float3 inVec)
{real dp3 = max(FLT_MIN, dot(inVec, inVec));return inVec * rsqrt(dp3);
}
所谓Safe,就是检查了一下向量的长度的平方,是否为0,如果小于FLT_MIN则取FLT_MIN,即最小的正32位浮点数。rsqrt
这个hlsl函数计算平方根的倒数,和原向量相乘就做了归一化。这个函数可以避免向量长度过小引起的除0错误。
- 之后计算shadow coord,如果已经在顶点shader里面计算了直接从varying获取,或者如果定义了
MAIN_LIGHT_CALCULATE_SHADOWS
则使用TransformWorldToShadowCoord
将世界空间位置变换到主光源空间,否则就是不使用阴影直接填充0。 - fogCoord和vertex lighting都是直接从varying获取,之前在VS里面计算好了。
- bakedGI根据是否使用lightmap从lightmap或SH中获取全局光照的颜色值:
// We either sample GI from baked lightmap or from probes.
// If lightmap: sampleData.xy = lightmapUV
// If probe: sampleData.xyz = L2 SH terms
#if defined(LIGHTMAP_ON)
#define SAMPLE_GI(lmName, shName, normalWSName) SampleLightmap(lmName, normalWSName)
#else
#define SAMPLE_GI(lmName, shName, normalWSName) SampleSHPixel(shName, normalWSName)
#endif
- normalizedScreenSpaceUV 是归一化的屏幕空间UV,这是用于采样屏幕空间的uv坐标,比如SSAO会用到。
- shadowMask是Mixed Lights的shadow Mask Lighting Mode相关的。在这种模式下面,会采样一张shadow mask贴图来获取物体上一个点最多和4盏灯的遮挡关系,以决定该点是否能被某个灯照射到。
#if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)#define SAMPLE_SHADOWMASK(uv) SAMPLE_TEXTURE2D_LIGHTMAP(SHADOWMASK_NAME, SHADOWMASK_SAMPLER_NAME, uv SHADOWMASK_SAMPLE_EXTRA_ARGS);
#elif !defined (LIGHTMAP_ON)#define SAMPLE_SHADOWMASK(uv) unity_ProbesOcclusion;
#else#define SAMPLE_SHADOWMASK(uv) half4(1, 1, 1, 1);
#endif
从上面的关键字可知,必须开启lightmap且开启shadowMask才会从贴图采样,否则会直接使用unity_ProbesOcclusion,这是保存在Light Probe中的光源遮蔽数据。如果不使用shadow mask模式,那么返回的mask就是1,表示没有阴影。
本篇小结
本篇分析了InputData这个结构的所有成员,这个结构是几乎所有的URP lit shader都会用到。这些成员会在最终计算光照,混合实时光照和Baked GI时用到。下一篇是SimpleLit的最后一篇,看看所有这些光照是如何完成的。
深入URP之Shader篇8: SimpleLit Shader分析(4)相关推荐
- 深入URP之Shader篇5: SimpleLit Shader分析(1)
SimpleLit.shader 本篇开始分析simple lit shader.我们通过分析unlit shader了解了URP shader的结构,以及一些基础功能.而simple lit sha ...
- 深入URP之Shader篇3: Unlit Shader分析[下]
Unlit shader 上篇中我们分析了Unlit shader的Properties在ShaderGUI中的处理,接下来看Sub Shader. SubShader unlit shader以及其 ...
- UI优化策略-Shader篇
原文:https://zhuanlan.zhihu.com/p/33458843 前言 优化本身是一件琐碎且耗神的事情,需要经历问题定位.原因探查.优化方案设计和实现.效果验证.资源修改多个步骤,也会 ...
- 【Unity3D Shader编程】之五 圣诞夜篇 Unity中Shader的三种形态对比 混合操作合辑
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...
- 【shader】UE4 Subsurface Profile shader提取
尝试把UE4里面的人像提取出来(因为无法直接获得UE4使用的shader代码),这个文章是基于UE4使用的sss.此外还有其他的sss呈现方式. 原作连接https://docs.unrealengi ...
- 【Unity3D Shader编程】之十三 单色透明Shader 标准镜面高光Shader
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/50878538 作者:毛星云(浅 ...
- 【Unity3D Shader编程】之十三 单色透明Shader 标准镜面高光Shader
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...
- shader 获取法线_Unity Shader 入门到改行5——法线贴图
the best of blur 1. 法线贴图理论 1.1 什么是法线贴图 一般的贴图中存储的是表面颜色值(RGBA),而法线贴图存放的则是法线信息(xyzw),假设某顶点处的 uv 坐标为 (u, ...
- 关于Unity 5.x Lightmap Shader 编译过慢的分析
关于Unity 5.x Lightmap Shader 编译过慢的分析 缘起 最近在研究Unity 5.x 的 Lightmapping 与 场景打包的相关的知识.发现坑深啊.关于怎么打包怎么用网上的 ...
最新文章
- Deep Web爬虫
- 动态为程序指定快捷键
- OpenGL编程指南13:光源移动
- java.util.Date和java.sql.Date之间的相互转换
- 数字通信原理_光耦继电器在实际应用中的作用以及工作原理!
- ArcFace免费人脸识别 Demo [Android]
- 源代码安装mysql配置步骤
- 删除XP防盗版补丁(转)
- 二分搜索/查找(最大化or最小化问题)
- 使用PL/SQL Developer 远程连接Oracle数据库出现 “无监听程序“错误 的解决办法
- Fermi架构专业卡登陆苹果Mac Pro
- Java 垃圾回收机制(面试高频问题之一)
- 仿真工具NS3的基本知识
- 视频教程-射频工程师工作指南-无线通信
- 拼音字母匹配排序php,小程序实现仿通讯录,拼音字母排序
- 集群通信组件Tribes之整体介绍
- linux ubuntu开放端口查看,ubuntu/linux下查看端口使用情况
- 使用muscle进行多序列比对
- 微信小程序父子组件传值问题
- 英语差python好学吗,英语不好可以学习编程嘛?
热门文章
- .plist文件里的Bundle versions string, short 跟 Bundl
- c语言中字符切割函数split,c 语言 指针 C语言字符串分割函数split实现
- 强生医疗科技旗下倍绣生物投1.5亿元扩产;美的推出首款家庭服务机器人小惟 | 美通企业日报...
- 在ubuntu 18.04上面用docker搭建家庭影院jellyfin
- KM算法的实现和封装
- 计算机信息继续考试 d组教材,无为县“备好课”专题培训考核小结
- OpenCV AI Kit(OAK)官方答疑录
- mathtype 复制粘贴到word中公式显示不全的问题解决方法
- php 竞拍系统,在线竞拍系统的PHP实现框架
- Axure高保真原型|音乐播放器原型