写的超牛逼!

unity shader base pass and additional pass - rexzhao - 博客园 (cnblogs.com)

总的来说就是光照计算方式有很多,有的可以效果牛逼,有的性能牛逼。

Unity默认是前向渲染,用的也比较多的。性能牛逼

顶点照明效果差,早期的,但是跑起来性能高,早期设备能力跟不上。也就是三四都是较为过时了的。

还有法线贴图等操作,也不能玩了。

这个基本上就是从性能出发的,效果上做了很多的取舍。

延迟渲染的方式,他需要在SubShader中写两个pass,其中一个是几何处理的pass。

他最后会输出几个结果RT,render target渲染目标。

输出的结果,可以认为是纹理图片,意味着存储的都是像素级别的信息。其实这里图片的像素就是和屏幕上的像素一一对应的,屏幕像素要计算出显示的颜色。

根据啥计算,就是根据图片中存储的这些值来进行计算。

这些图片具体的存储内容,就如上,前三个就是一些基本的信息。第四个中,有一个lighting??lightmap是因为,如果这里采用了间接光烘焙的效果,那么这里自然需要从lightmap中采样获取。

对于RT4,这个只有开启烘焙模式为shadermask的时候,存储一些mask信息。

所以说这个几何处理 每一帧都会出来这些纹理图,内存是不小的。

当延迟渲染路径不被支持,然而你使用的shader恰好是延迟渲染,这时候会fallback到前向渲染。

==========================================================================================================

我们可以通过以上2方式来决定渲染方式。

这里是我们设置渲染路径,意味着渲染管线就按照我们设置的路径来执行,而在shader里面也有一个渲染路径的设置:

写上这么一句。

我们在pass里面如果不写上边这种形式的语句。那么意味着这个pass,可以在任意的渲染路径下执行。无论外头设置的是啥渲染路径,这个pass都会执行。

因为一个Subshader里面的所有的pass都会执行的。有可能你写了俩延迟渲染的pass,但是你没有写上边的tags,外头设置的是前向渲染路径的情况下,他还是会执行这个pass的。

那么用前向渲染的流水线来执行延迟渲染的pass结果肯定就不对了。

所以我们延迟的pass,就写上tag,打上延迟渲染的标签,这样在前向渲染的流水线下就不会执行这个pass。

如果没写就默认按Always处理。

这里可以看到渲染路径的一些信息。

延迟渲染中我们可以直接切换第一个pass输出的RT。然后视口中直接就互展示出这个RT的结果。

=================================================================================================

最后一个面光源,没有实时效果,必须烘焙才有效果。

==============================================================================================================================

这里工程中像素灯的数量是我们可以设置的一个值。

首先我们写shader,我们要对灯光的类型要做到心中有数,尤其是在写forward pass的时候,因为逐像素,顶点,球谐都是会用forward的 pass。

其实这里展示了一个什么功能呢?

其实就是一个物体的材质shader可能写了好几个pass,虽说是每个pass理应都执行,但是上边这个情况的时候,就会选择执行。

那么我们根据光源不同类型,然后加上important的标记,再加上max逐像素数目的设计,可以控制光源计算的方式,逐像素还是顶点球鞋。然后控制住计算的方式,根据不同方式会有的选择,去推断他们执行的pass的类型,然后根据想要对该光源使用的shader贴上对应的tag。这样就实现了对该光源使用哪一个shader的控制。

项目中设置最多的逐像素灯的个数在上边的位置。

这里还有一个原则,就是无论如何,场景内最亮的那个平行灯,一定是逐像素的,尽管上边的最大数量设置成了0.

除非一种情况,就是,你把这个最亮的平行灯,改成了not important。

在这里我们可以看到他走的是SH球谐的计算。

切换到game,这个frame debug才会有效果。

上边这个看效果还是keyword都可以看出来是一个顶点照明和球谐。这里是因为我们设置的像素灯数量为0.不管他是不是最亮,都会是非像素的,除了刚刚的例子是一个最亮的平行灯。

如果改成max数量改成1,那么一定会是像素,他又不是平行灯,所以就forward add了。

这里是走了两个pass,第一个Draw Mesh Plane是球谐

然后这里我们往场景里面加一个逐像素,四个逐顶点。

第一个pass:

这四个灯都会在一个pass中计算出来。

第二个pass就会计算逐像素灯的效果。

如果说有多个逐像素的灯。那么每一个就会有单独的一个pass。

所以性能消耗主要在逐像素的灯上,因为逐像素的灯,每一个灯走一个pass,而逐顶点或者球谐的灯都放在一块走一个pass。

这里除了逐像素的灯,加了五盏灯,这里unity最多允许四个逐顶点的灯,然后另外一个就走球鞋了。

这里如何确定谁走球鞋,就是比一下谁距离中心最近。

=================================================================================================================================

======================================================================================================================================================

我们通过frame debugger里面可以看到这个生成的shadowmap,但是这个unity引擎生成的似乎和我们想象的并不一样,是因为unity内部用了更高级一点的方案。cascade级联阴影。

这个就是近处的影子清晰度高,远处的低一些,所以存储shadow map的时候是有些不同的。

在项目设置中,我们可以修改是否采用级联阴影。

这里也改一下,这样就会取消了。

这里会有游戏视角下的生成的深度图,也就是这一帧的zbuffer。

============================================================================================================================

阴影主要分为两个部分,一个是物体投射出阴影,一个是别人投射的阴影在自己身上显示。

注意这里appdata里面的顶点位置和normal法线名字一定按照上边的去写,因为下面我们添加一些宏指令,他们会默认我们这么写。然后从中取数据,所以我们这里必须叫这个名字。

这时候出现了一个报错。

上边少了一个宏,我们得再弄上这个。

这个错误,我也出现了,但是我加上上边这一句没能解决,没有反应,反倒加上UnityCG.cginc解决了,而他在视频里尝试加上UnityCG.cginc却没反应。很怪!

阴影的部分,就是我们多写上一个pass。

我们是可以控制是否投射/接受阴影

这里利用alpha 透明测试,实现了一个溶解效果,而我们的阴影却没有溶解。

我们需要一个随着模型逐渐溶解的效果,也就是把这个alpha测试考虑到阴影中去。

其实也是在片元shader刚开始的时候加一句clip。

=================================================================================================================

这一个是采样阴影,就是我们在绘制模型本身的时候,去shadow map 采样一下,看看是否处在阴影中,然后决策出最后的颜色。

所以说这一个步骤是要写在模型渲染的pass里面的。

刚才那个投射阴影就不一样,它另外开一个pass,因为它投射和自己本身的渲染没有关系(这里好像不能说完全没有关系,因为有一个自己的胳膊阴影投射到身上的现象。)

估计着,应该是整个场景中所有的投射阴影的pass都会先执行,然后会得到汇总的shadowmap图,说原理的时候是直接光源视角生成一个场景的shadow map,但是实际应该是去除不投射阴影的物体的场景,我们对他弄一个shadow map。

这里就是声明一个变量_ShadowCoord,类型unityShadowCoord4其实就是float4。这个变量就是后面要用的变量。

定义一个变量,然后把变量进行一个变换,变换到采样shadowmap的坐标。

然后就是采样shadowmap,我们这个UNITY_LIGHT_ATTENUAITON,之前光照衰减的时候是用的这个宏,这里也用的这个,其实他俩是被放在了一起,都可以认为是光的衰减,从它的实现上也可以看出来,这里就是算了一个阴影的结果,然后乘以应该是光照衰减的结果,作为最终的一个衰减,平行光不会衰减的时候,tex2D那一项估计就是1了。

我们关注的在于UNITY_SHADOW_ATTENUATION,这个宏往下追踪,最后到:

其实就是采样shadow Map。所以这几个宏分工大致就是这样。

但是但是,

注意上边的那几个宏的使用,都是没有加分号的。

我们按照上边的方式,给我们的渲染pass里面加上上边三行之后,并没有出现接受阴影的效果。

原因之一是,我们计算出来了atten,他是一个衰减,我们需要把最终输出的颜色去乘上这一个数,才会有作用。(这里截图少了一句,缺少AutoLight的头文件, )

但是这时候乘上,还是没有作用。这时候就是debugger发挥了:

首先明白UnityDebugger的玩法,

(116条消息) [Unity Shader学习笔记]帧调试器(FrameDebugger)的使用_曾胖神父的博客-CSDN博客_framedebugger

他这里的Draw模块,是从上到下依次进行draw渲染的。

那么我们这里啥都准备好了,但是没有接收到阴影,这里我们采用的方式就是往场景里面拽一个球,球是可以接受阴影的,那么我们找到Draw Mesh Sphere,再找到我们的Draw mesh(选中不同的draw,显示的当前帧的内容就会有变化,可以根据往下点一个draw,屏幕多画了一个啥来判断哪一个命令对应哪一个物体。)

找到之后,我们对他们

这里是缺少了一个宏,所以就需要添加一个宏。

添加这个预编译指令即可。

这个时候还是有问题的,打开我们的shader面板。

这里的变体会非常多,是因为我们加了上边那一句。意思是它里面包含了很多,但是有大部分都不是我们需要的。

刚刚有阴影的球,他是有SHADOW_SCREEN和DIRECTIONAL的,我们其实只需要和他一样,加上这俩就够了,其他的都不需要。

我们只需要复制上边的那一行。然后删除掉我们需要的那俩。

加上这一句即可,也就是剔除掉一些变体,skip_variants后面加上我们需要剔除的,也就是用不上的。

还有一种方式来处理这个,我们不直接#pragma multi_compile_fwdbase。

而是直接自己把需要的两个添加到程序中即可:

这样变体

这里我的compile code这里点不开,点一下就报错,报错还没找明白为啥!

number of parameters specified does not match the expected number.

有时候由于视角变换等等的问题,会导致阴影没了,是由于缺少了前向渲染的pass表示,以后做光照相关的时候,一定声明这个,否则有些宏会有问题,会进入一些错误的分支。

写上之后,这里就出现了。

另外它前面的#1,代表是的它走的是第几个pass,这里从0开始算的,

这是他的shader,他在这个pass之前,还有一个UsePass,所以是第二个。

这种标准的球,他们的这里写的就是走的FORWARD的pass。(standard中对这些pass好像命名了)

还有一个问题,就是阴影的亮度:

灯光中有一个选项,可以调节实时阴影的强度。

1的时候非常黑,但是通常情况下,我们就是弄成1,关于它非常黑的问题,会随着加入GI之后改善。

现在模型已经有了投影和接受阴影,这里还有一个问题:

就是把这个投射阴影的pass给注释掉,这时候会发现不光是投射的阴影没了,而且接受的阴影也没了额。

这是因为shadowcast出了问题。

如果想要正确的投射接受阴影的话,

必须再render shadow map这一步有一个正确的操作,

他这里先进行了清屏,

然后这里就是绘制shadowmap,做完了这三步之后,会发现没有主角的模型。

是因为我们注释掉了主角的shadowcaster,这是一个渲染队列,注释了,主角就不会进入这个渲染队列,这里自然也就不会把他考虑在内。

然后下面的投影和接受阴影就出错了。

这里第二个pass如果我们只写一个

Tags {"LightMode" = "ShadowCaster"}

他也是可以投射和接受阴影的。

可能是我们剩下的空pass,他会帮我们补全。就是根据这个LightMode渲染路径进行的补全。

反正这里的意思好像就是接受和投射阴影实现虽然是分开的,但是他俩必须同时存在,要么既有投射又有接受,要么和阴影根本不沾边。

-============================================================================—————————————————————————————————————————————————

我们把shader里的第二个投射阴影的pass关掉之后,会得到上边俩图的效果,就是看似阴影投射到了小人身上,但是实际没有。这里第一张图,小球本应该把柱子投射的影子给遮挡住的。所以这里是有一些问题的。

究其原因,这里我们直接看渲染的深度图,发现主角是没有写入深度图的。这里又说明了一点,就是shadowcaster除了之前说过的作用之外,这里还有一个作用就是控制是否写入深度图。

同时这里的shadowmap自然也没有被写入。

具体啥原理造成上边的结果,没想明白。

考虑到第二个pass可能有很多的shader都会用到,当然可以把他写到一个单独的shader里,以后usepass。或者是把他直接来回copy。

还有一种方式就是利用fallback:

打开这个shader。

这个是很多的shader都会fallback到最后的一个去处。

里面就有一个shadercaster。

========================================================================================

高中低配的阴影。

高配,我们采用实时的阴影,中配和低配就简化掉。

Shader LOD是针对一个Subshader来操作的。

LOD之间的切换是通过C#脚本来实现的,程序控制c#脚本来实现。

这里的高中低配就分别用600, 400,200来表示,我们只需要:

准备三个Subshader,然后对应写LOD,这样就是三个Subshader,程序判断来控制什么时候LOD进行切换,程序去告诉c#脚本,c#脚本就会修改globalMaxmumLOD,然后就会执行不同的Subshader。

直接把脚本拽到相机上边。

然后the quality可以切换高中低配

他的逻辑是:看到这里设置的高配,会到shader中找对应的SubShader,因为高配就是找<=600的Subshader,注意是从上往下找,而且找的不是等于600,而是小于等于。如果把400放在第一个那么就会执行400的。所以注意前后关系。

如果找不到好像也是会进入fallback中找的。

===========================================================================================================================

这里我们对中低配的阴影进行一个实现。

网格阴影的实现思路,unity朝上的轴是y轴,然后我们把模型在y轴上给他压扁,然后以某一个颜色绘制出来,接下来要对这个压扁的影子做一个偏移,如果是单纯的偏移,所有部位都偏移相同的距离,那么效果不是很真实。

所以我们可以让他们在偏移的时候,根据他们y轴的值来确定偏移的量,就形成了图三的效果。

这里一个pass做角色渲染,一个单独的pass做阴影的渲染。

这个优缺点,有些场景下可能就适合使用,有些可能不适合。

对于中配的Subshader,第一个pass,直接和高配一样就行,但是要从中删除阴影相关的代码。

我们要把模型拍扁,其实就是把y值都改成某一个相同的值,有两个空间可供选择,世界空间下拍扁,模型空间下拍扁。这里应该是世界空间下,因为考虑一些带有旋转变换的情况,我们模型空间下拍扁,效果就变成了,先拍扁,然后再旋转,那样效果就不对了。而且我们说的沿着y轴拍扁,其实这里说的就是世界空间下的y轴,因为影子要水平的。

这里除了角度问题基本上效果已经ok了。

实现的方式就是:把模型顶点的y值都写成0,然后0不一定在脚下,所以影子还需要上下平移,这个平移的值,我们开放成一个参数,便于调节。

然后平移拉伸的思路就是朝着一个方向加上一个值,然后这个值和自己的高度成正比。注意此高度非y值,脚底高度应该认为是0,也就是y值-_Shadow。

然后关于影子的角度,也就是上边那个fixed2的控制。这里其实也开放出去,并且和shadow放在一起,

全都放在这一个变量里面。

最后还有一点,就是对于阴影的颜色,其实阴影并不能是纯色的,他其实是和地板本身颜色的一个混合。所以我们这里把刚刚_Shadow的第四个分量给利用一下,里面装一个alpha,然后搞一个混合操作。

修改之后,阴影又出现问题了,其原因就是 模型压缩之后有些部分是重叠的,所以出现了第一次混合之后,又来一次混合。

这里可以通过模板测试来解决。

学习笔记07渲染路径+阴影相关推荐

  1. JavaScript学习笔记07【6个经典案例——电灯开关、轮播图、自动跳转首页、动态表格、表格全选、表单验证】

    Java后端 学习路线 笔记汇总表[黑马程序员] w3school 在线教程:https://www.w3school.com.cn JavaScript学习笔记01[基础--简介.基础语法.运算符. ...

  2. MySQL学习笔记07【事务、用户管理和权限管理】

    MySQL 文档-黑马程序员(腾讯微云):https://share.weiyun.com/RaCdIwas 1-MySQL基础.pdf.2-MySQL约束与设计.pdf.3-MySQL多表查询与事务 ...

  3. 【EF学习笔记07】----------加载关联表的数据 贪婪加载

    [EF学习笔记07]----------加载关联表的数据 贪婪加载 讲解之前,先来看一下我们的数据库结构:班级表 学生表 贪婪加载 //贪婪加载 using (var db = new Entitie ...

  4. 《机电传动控制》学习笔记-07

    <机电传动控制>学习笔记07 胡恒谦 机卓1301 (注:本周补上第7周的学习笔记) PLC的编程元件: PLC内部有许多不同功能的器件,实际上这些器件是由电子电路和存储器组成的. 1.  ...

  5. Liunx学习笔记 - 07 - 02 正则表达式与文件格式化处理

    Liunx学习笔记 - 07 - 02 正则表达式与文件格式化处理 1 前言:啥是正则表达式 简单来讲,正则表达式是处理字符串的方法,它是以行为单位来进行字符串的处理行为,正则表达式通过一些特殊符号的 ...

  6. JavaWeb黑马旅游网-学习笔记07【旅游线路查询】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  7. MIPS汇编语言学习笔记07:打印双精度浮点数

    课程原视频: https://www.bilibili.com/video/BV19J411y7pA?p=7 程序功能: 设定两个双精度浮点型数据,将其相加后打印输出. 代码: .datamyDoub ...

  8. 多路径配置udev_学习笔记:Linux多路径配置 multipath实现设备用户组绑定详细设置...

    天萃荷净 Linux多路径软件配置,通过multipath实现设备用户组绑定详细设置 现在的Linux系统中,很多都会使用系统自带的multipath多路径软件,在以前的版本中,我们一般通过multi ...

  9. Makefile学习笔记07|编译静态库并通过ifeq语句

    Makefile学习笔记07|编译静态库并通过ifeq语句   希望看到这篇文章的朋友能在评论区留下宝贵的建议来让我们共同成长,谢谢.   这里是目录   本篇与上一篇有较多联系,有兴趣的可以先看上一 ...

最新文章

  1. 字节数组拼接打印以及list小技巧
  2. fanuc机器人与plc的通讯_S7-1200PLC与FANUC机器人Profinet通讯方法
  3. 修改Linux安装软件镜像源为阿里云
  4. 如何在移动设备上调试html5开发的网页
  5. 数据科学家 数据工程师_数据科学家应该对数据进行版本控制的4个理由
  6. “约见”面试官系列之常见面试题之第八十四篇之手写promise(建议收藏)
  7. Scala程序将字符串转换为整数
  8. js中prototype用法
  9. 深入浅出看懂AlphaGo Zero - PaperWeekly 第51期
  10. [转载] 用python 获取当前时间(年-月-日 时:分:秒),并且返回当前时间的下一秒
  11. 传统数据库在分布式领域的探索
  12. 解决远程服务器ssh登陆慢等问题
  13. bootstrap modal 关闭时右侧滚动条消失,页面左移的解决方法
  14. VC被控制时关闭极域电子教室、破解联想硬盘保护系统密码(上)
  15. python 操作ps脚本_Python实现PS图像调整颜色梯度效果示例
  16. CRM如何管理企业销售流程
  17. MySQL中增删改查的例子
  18. python风控模型举例_一文搞定风控模型6大核心指标(附代码)
  19. 中国K12在线教育用户消费行为报告
  20. Android 实现图文混排

热门文章

  1. IDEA黄色警告解析集锦 - 类名【xxx】应以Impl结尾
  2. 树莓派引脚驱动编写(2)
  3. KingCoCo KC600 120G固态硬盘量产开卡实录(主控SM2258XT,29F48B2ALCMG2颗粒)
  4. 第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛 I 买花
  5. 智慧电厂信息系统设计方案(下)
  6. OpenAI GPT-2 官方模型下载
  7. PHP付费资源下载交易平台网站源码/整站源码带数据库
  8. Bootstrap图标库的下载及使用
  9. win11edge怎么卸载?彻底卸载edge
  10. ps怎么给文字添加颜色,做出纹理化效果方法