无题
UnityShaderGraph
Youtube教程UnityShaderGraphBasics
创建
首先我们创建一个universal render pipeline的项目
进入项目,我们可以在package Manager当中查看Shader Graph插件是否已经正常安装
之后
我们在资源管理面板当中选择符合自己项目渲染管线的shader Graph
双击之后,unity就会打开这个shader graph
之后你可以调整fragment片元着色器输出的颜色
获取挂载了这个shadergraph的材质
在shadergraph里面创建属性
调整颜色之后赋值到场景中的物体上
纹理
右键或者空格快速创建节点
创建了之后,我们把我们创建的texture属性连接上sampleTexture2D,一般来说,unity会自动在内存空间当中帮我们申明一段uv(也就是uv0)
之后我们将颜色和纹理的rgb值进行相乘
注意:
制作简单的uv动画
我们创建了一个scrollvelocity属性
之后创建time节点和multiply节点
这个结点可以计算uv的偏移
透明
queue值更低的物体绘制顺序更加靠前。一般来说透明物体距离摄像机越远它的queue值会越大,也就是越远的物体越先进行绘制,这样会牺牲一定的性能
要把透明物体的深度写入关闭,并且保证不透明物体是先进行绘制的,防止有物体读取到透明物体的深度值然后发生剔除
Shadergraph会自动替我们为透明物体设置渲染队列
在graphsetting里面将物体的surfaceType设置成透明
之后我们就可以得到alpha混合的效果
alpha clipping可以将不透明度低于某一个值的片元直接进行裁剪
首先我们要在Graph Setting调整Surface Type为Opaque,并且勾选alpha clipping
如图我们将一个png图片(这个图片中间透明度最低(alpha高),两边透明度最高(alpha低),以圆的方向扩散开),可以看到alpha值小于0.5的都被剔除了
之后我们创建一个threshold属性然后连接alpha clip threshold,之后将这个属性mode设置成slider滑块,然后我们就可以在inspector当中调整透明剪切的程度
使用alpha裁剪的不透明物体有剔除像素的能力但是太暴力了
我们可以使用一种叫做dithering的技术来伪造透明度
shadergraph中创建dither
将x设置成1
之后我们就可以在界面来调整了
深度
Auto代表对不透明物体进行深度写入,对透明物体不进行深度写入
ForceEnabled代表强行对所有的物体进行深度写入
ForceDisabled代表不对透明物体进行深度写入
Depth Test
它决定了深度值的比较方式和是否写入像素值
LEqual代表了如果物体更近那么就pass
always代表不论深度缓冲区中的值是多少这个物体总是会被绘制
Never代表任何时候深度缓冲区的比较都会失败
Greater代表只有当这个物体前面有更近的物体(更小的深度值)这个物体才会被绘制
这个按钮可以让你在inspector窗口当中modify这些depth setting
我们的shader一般不能直接对depth buffer中的值进行读取,而是在所有的物体都被绘制之后
unity会将这个这一帧的深度值buffer的状态保存为一个摄像机深度纹理
我们shader可以获取这个深度纹理
注意
这个纹理只会包含有用的信息如果我们在写一个透明的shader
这个纹理不会包含有关透明物体的深度值
在URP当中使用这个纹理
我们需要激活它
第一步:找到你的项目使用的URP assets。如果你使用的URp模板创建的项目,那么这个资源在Assets/Settings
选择其中的每一个并且将其的DepthTexture勾选上
之后我们创建一个新的ShaderGraph
第一步我们需要将Graph Settings里面的Surface Type选择上Transparent
之后我们需要添加新的颜色属性:前色和背景色
创建一个叫做scenedepth的node
之后将模式选择成raw,这个时候我们就可以获取depth values了
如果我们调整成Linear01
那么depth value就会线性化
他和raw value一样都在0和1之间,但是通过映射得到了相反的效果
在 Unity 的渲染管线(如 URP、HDRP)中,Z-Buffer 存储的深度值通常是 非线性的(透视投影下)。使用 Linear01
可以将这些深度值转换为 线性 0~1 之间的数值,使其更适合用于后处理计算(如景深、雾效)。
相机深度值代表的是从相机到物体的真实距离,以世界单位来衡量(Unity的米),而不是0~1归一化的深度值
在eye模式下,近平面near->深度值接近0
远平面深度值变大(单位是米)
Lerp节点与math中的lerp类似,在两个值之间依靠一个0~1的比例值进行线性变化
之后我们可以在场景当中调整深度图的颜色,获得一些渐变效果
之后可以把材质拖拽到物体上
之后让摄像机对着这个物体,我们可以看到这个物体后面显示了深度值(被设置成了线性的粉色)
顶点
我们创建两个值来模拟顶点动画
我们将会让顶点根据sin值上下浮动,其中speed代表速度(频率),strength代表幅度
但是如果仅仅是这样,我们只会让全部的顶点根据正弦函数上下移动,而模拟不出我们需要的波浪效果
我们可以将顶点在世界空间当中的坐标作为offset偏移值来模拟波浪效果
你也可以使用模型空间,但是在世界空间下,它们可以更好地合并在一起,如上图所示
tessellation(曲面细分)
URP Shader Graph貌似不支持tessellation
hdrp是camera relative rendering,所以是绝对世界坐标
之后通过更改x的值,附上材质的物体将会添加曲面细分
灯
与之前创建unlit shader graph不一样,这次我们创建了一个lit shader graph
我们找到一个具有许多附加纹理的纹理
依次是反射颜色贴图,粗糙度贴图,环境光遮罩贴图,位移贴图,法线贴图
高度贴图
连接到一起
法线贴图
金属
可以看到在Graph inspector当中,这里有两种工作流,Specular和Metallic
两者的区别在于
创建一个名为Metallic的属性
连接到fragment上面
粗糙度贴图
自发光
添加一个叫做emmision color的vector4
将其设置成HDR
这允许我们使用更高密度超出通常选色范围的颜色
一般来说你需要通过bloom后处理效果才能得到自发光物体的泛光现象
但是拥有URP的项目可以直接调用bloom
之后我们会在Hierarchy面板当中看到新建了一个Global Volume物体
之后我们可以添加override来得到更多的后处理效果
环境光遮蔽
有时候我们不需要整个物体都拥有高光效果,比如说这个物体上有一些泥巴或者坑坑洼洼的小洞,那么相对于金属表面来说这些地方的反射强度就会弱一些
最终结果
更多灯
菲涅耳反射
它事实上是一种类型的镜面反射
主要是这个power可以控制菲涅耳反射的力度
使用菲涅耳反射模拟物体高亮
添加属性,用来控制菲涅耳反射强度
添加菲涅耳颜色。并且设置成HDR
注意这个intensity实际上是指数,pow(color,intensity),所以当intensity为0的时候即没有HDR
有时候这下面只是一些非常相似的颜色,这事实上并不是bug,而是intensity太高的缘故
因为硬边立方体的法线布置比较失真,所以效果不太好
球体的效果
NPR
一般来说,不使用lit shadergraph来模拟非真实感的效果,因为lit的可操作性太低
一般使用unlit来模拟npr
接下来是对卡通渲染来说至关重要的阈值阶段
有两种方法来处理
第一种涉及到Step节点
否则输出1或者白色,在数学中这被称为阶跃函数
而smoothStep,中间会有一层缓冲区
smoothstep模拟npr光照
这个ambient light strength是一个浮点值
之后我们就可以在inspector面板里面进行光照调整了
场景交叉的环境光遮蔽
更多的环境光遮蔽
我们在现实生活当中,会发现当两个物体相交的时候,这两个物体之间存在阴影
如何检测交叉点
当shader正在运行的时候,它只能访问有关当前正在渲染的像素的信息。
包括其在世界空间当中的位置,我们希望将该位置与其后面渲染的下一个对象的位置进行比较
如果两点之间的距离小于我们给定的阈值,那么就说明我们检测到了交点
这确实带来了一些限制
首先我们的交叉着色器必须是透明的,因为unity仅在渲染所有不透明物体之后和渲染所有透明物体之前,将深度缓冲区的状态保存到深度纹理当中,其次,出于同样的原因,我们的着色器将会无法检测任何两个透明物体之间的交点
获取相机到地板的距离
由于地板是有深度值的,我们将会使用场景深度节点来获取相机与之前在此处渲染的对象之间的距离
注意要将Samplin设置成Eye模式,精确地获取到这个距离
获取相机到物体表面的距离
由于物体设置的是Transparent模式,它的深度没有进行写入,所以我们需要使用一些特殊的处理方法
回顾一下裁剪空间的概念
在裁剪空间当中这个w值的意义是相机和正在渲染的顶点之间的距离
透视除法之后的z/w才是深度值
之后我们得到了照射到floor的距离(蓝色),以及到物体表面的距离(橙色)
之后我们将其详见获取器紫色部分的值
shader使用技巧复制粘贴
使用子图来进行重用节点
选中这些节点右键
之后我们可以将它保存在任何我们想要的地方
之后我们可以在project view当中双击进入subgraph进行编辑
可以看到它必须要一个输出不然就会报错
这个子图默认不需要输入,但是我们仍然可以像常规图添加属性一样来添加输入
点击此处将新的输出添加到列表当中
该子图的唯一输出僵尸表示交叉点长度的距离值
该子图的唯一输出将会是表示交叉点长度的距离值,于是我们可以添加一个float类型的输出
回到我们的shadergraph
注意要将Surface Type选择为Transparent
如果我们将这个函数连接到basecolor,球体网格看起来边缘会是黑色的
因为在靠近地面的地方,距离值几乎为0,这个时候差不多就是黑色,上面的部分没有交点,几乎就是白色
相反。我们想将其转换为一个值,其中1代表交叉点的全部强度,并且当我们距离交叉点越来越远的时候,它会变得越来越低
这个时候我们就会使用One minus,但是有时候这会变成负数,因为白色部分有很多地方都是与背面物体的距离大于1的。
所以我们会使用到saturate
接下来我们需要控制交叉口的宽度
有很多的方法可以做到这一点,但是目前我们处理的是0 到 1之间的值
所以最简单的方法可能是将之提升到可以配置的Power Value(指数值)
其中0会将完全遮挡应用于整个网格,而25是一个任意值,会导致非常薄的遮挡
这个浮点属性来添加使遮挡整体变亮或者变暗的功能
这个可以是0到1之间的滑块
它将会作为我们迄今为止计算的值的全局乘数
到目前为止,我们仍然有一个0到1之间遮挡强度的值,其中0代表没有遮挡,1代表遮挡程度最高
由于各种浮点运算,这个值将会难以达到1
之后我们可以在inspector窗口当中调整边缘环境光遮蔽的强度
我们可以通过这种效果来软化岩石与地板之间的边界
这是一个非常基础的屏幕空间的环境光遮蔽
更加elegant的解决方法是使用被渲染像素周围几个像素的深度值以更加准确地了解像素周围物体的形状
场景交叉2:制作波浪泡沫和边缘GLA
注意blender
注意blender当中使用的坐标系与unity不同,unity是左手,blender是右手,在导出高精度平面的时候要应用变换
复制一份之前的wave shadergraph
引入之前的子图
我们需要在深度差异较小的地方应用浮沫
这将不同于交叉遮挡着色器
创建一个滑动条属性控制浮沫,数值越大浮沫的长度越大
当泡沫距离变小的时候divide节点的输出会变大
我们可以使用一个step节点,如果in小于edge则输出0,否则输出1
只有当深度差小于浮沫距离的时候,也就是相除结果小于1的时候,这个节点输出1,这个step才会被启用
回到shader graph当中,添加一个名为simple noise的节点
之后再添加一个float的foam scale,velocity属性。
解决直线边缘
回到子图当中我们添加offset属性在屏幕空间上进行偏移模拟折射
保存子图
我们可以发现其它使用了这个子图的shadergraph出现了这个输入
添加属性
使用0到0.1的原因是他代表的是屏幕的比例
调整offset之后
但是仍然有bug
修正
因为shader会把你屏幕上显示的所有物体片元表面与物体遮挡的水面相减并且得到结果,由于我们是小于某个值(这个值在0到1之间)就会绘制浮沫,这个时候就会发生一些问题,所以我们要将其取反,并且根据这个距离重新进行计算。
之后
边缘发光
效果
这种效果在我们创建类似能量盾的物品的时候非常有用
思路,我们将任何轴上接近于0或者1的uv值都将被注册为物体的发光部分
之后这样做,把rg单独分离出来,并且相加(得到一个新的vector2)
之后获取另一边
事实上,这四个角落会凸显出来,因为发生了两个通道数值的相加
注意要保证shader的模式设置的是透明
现在的效果
现在的效果
这种方法的缺点所有的uv接缝都会发光,只能用于一些简单uv的物品
Custom Functions
之前我们使用MainLight来模拟了NPR的照射,但是有时候我们会需要多个灯光照射的结果
我们可以在shader graph里面使用自定义函数节点
我们需要在asset menu当中创建一个hlsl文件
其中ADDITIONAL_LIGHT_INCLUDED预处理指令可以防止Unity多次将你的函数包含在生成的代码当中
在里面我们可以自定义任何我们想要的函数,而自定义函数节点可以通过名称调用其中的任何一个函数
每一个函数都需要以特殊的方式进行编写
我们可以在nodesetting当precision来设置它
attenuation为1代表这个光源离物体很近被光源完全照亮
而0代表这个光源离物体很远不会被光源照亮
之后我们设定好这个函数
然后我们可以将其连接到diffuse上去
之后我们可以获得和之前一样的漫反射非真实感光照
我们将衰减和颜色混入光照当中
进行点光源的限制
ShaderGraph的限制
在着色器代码当中,我们可以进行循环
但是在shadergraph当中,则不能进行循环
我们为点光添加照明
将这些部分合并成子图
进入子图
我们复制出总共四个点光源
之后将这些光照叠加起来
效果
如果这时候我们加入第五个光源,由于shadergraph实际上只处理了4个点光源,移动这个光源可能会出现这个光源替换其他光源的bug
如果灯光少于4个,事实上这个shadergraph仍然会计算4个光源(shader会创造一个黑光灯来计算颜色)这会造成性能上的浪费
制作另一个计算点光源的函数
与之前的代码进行比较
这个代码致力于计算场景当中的所有点光源