unity-shader-思路与技巧
title: unity-shader-思路与技巧
categories: Unity3d-Shader
tags: [unity, shader, 思路, 技巧, 奇技淫巧]
date: 2019-05-01 00:49:32
comments: false
目的是为了想实现效果时, 快速找到实现的方案.
主要用 shader graph 实现, 可视化会更容易于理解, 部分会结合代码.
与顶点位置相关
-
使用 模型空间 顶点位置, 乘以一个系数 spread 来控制顶点位置的缩放, 加上一个系数 position 来控制顶点位置的偏移
顶点法线 Dot
一般都是用 dot(normal, vector3) 来获取夹角余弦值
以 积雪 效果为例
- 用 snow direction 与 法线 点乘, 主要控制哪个方向的面需要做效果
- 用 snow depth 和 step 控制夹角多少以内需要做效果
- 再结合 fresnel 做平滑过渡.
判断 相交
-
神奇的深度图:复杂的效果,不复杂的原理 - http://www.php361.com/index.php?c=index&a=view&id=5257
-
测试: 关键字: force field. 红色部分为 代码 实现, 黄色部分为 shader graph 实现, 两者逻辑一样.
-
相关代码
fixed4 frag(v2f i) : SV_Target { //获取已有的深度信息,此时的深度图里没有力场的信息 //判断相交 float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos))); float partZ = i.screenPos.w - _Offset; float intersect = 1 - (sceneZ - partZ); //圆环 float rim = pow((1.0 - saturate(dot(normalize(i.normal), normalize(i.viewDir)))), _FresnelPower); float smoothVal = smoothstep(0, 1, intersect); _MainColor.a = smoothVal + rim; return _MainColor; }
-
原理
- 判断相交. 用 自己的深度 与 场景的深度(也就是深度图的值) 比较. ( 因为是自身是 transparent, 所以不会渲染到深度图中)
- 圆环 rim. NdotV.
-
应用场景
- 水面与物体交界处
- 能量场
另外: shader graph 中的实现
-
之前看教程 scene depth 貌似 2019 版本的有 linear, eye 等选择. 但是这里是 2018 版本的没有. 可以通过与 camera 的 far plane 相乘 得到一样的数据. 参考: https://www.youtube.com/watch?v=ayd8L6ZyCvw
Scene Depth Node - https://docs.unity3d.com/Packages/[email protected]/manual/Scene-Depth-Node.html
切片效果
主要利用函数 frac
-
利用模型顶点的 y轴分量, slice value1 和 fraction函数 控制切片细分程度, slice value2 控制细分宽度
uv流动 的几种方式
-
基于 纹理uv. 模型顶点的uv值. (看起来有点乱, 因为 uv值 的是在 uv展开 的时候决定的)
-
基于 顶点位置, 又分为 切线,对象,世界,观察 四个空间的
-
观察空间下
-
-
基于 屏幕空间的位置. 简答的理解就是屏幕的 左下角是(0,0), 右下角是(1, 1), 顶点都位于这个区间内
基于 位置 (2和3) 的看起来贴图是贴在屏幕上的.
判断 边缘, 平滑, 宽度
smoothstep 函数用于 平滑, 宽度 计算
step 函数用于判断
-
基于 纹理uv
-
效果
-
代码
fixed4 frag (v2f i) : SV_Target { fixed val = tex2D(_Noise, i.uvNoise).r; fixed isShow = step(val, _BurnAmout); clip(1 - isShow - 0.0001); fixed4 mainClr = tex2D(_MainTex, i.texcoord); fixed4 edgeClr = fixed4(1, 1, 0, 1); fixed4 finalClr; // 方式c 平滑边界颜色 float edgeSmooth = 1 - smoothstep(val, _BurnAmout, _BurnAmout - _EdgeWidth); edgeClr *= edgeSmooth; finalClr = mainClr + edgeClr; return finalClr; }
-
-
基于 顶点位置
-
split value 控制clip的值, 及边缘的 边界值2, edge spread 控制边缘的宽度, edge spread 越小就越接近 边界值2, 也就是越窄
上半部分计算宽度及渐变, 下半部分计算clip
-
代码
fixed4 frag(v2f i) : SV_Target { float isShow = step(i.objPos.x, _DissolveThreshold); clip(1 - isShow - 0.0001); fixed3 mainClr = tex2D(_MainTex, i.uv).rgb; float edgeSmooth = 1 - smoothstep(i.objPos.x, _DissolveThreshold, _DissolveThreshold - _EdgeWidth); _DissolveColor *= edgeSmooth; return fixed4(mainClr, 1) + _DissolveColor; }
-
理解 边缘 效果思路
-
step
-
smoothstep
程序算法节点
可以参考文档里面的算法实现, 搬运代码时方便 - https://docs.unity3d.com/Packages/[email protected]/manual/Procedural-Nodes.html
顶点沿法线偏移
可用于 描边 pass, 只显示背面
代码
v2f vert_outline(appdata_t v) {
v2f o;
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 offset = normalize(worldNormal) * _Outline;
worldPos.xyz += offset;
o.vertex = mul(UNITY_MATRIX_VP, worldPos);
return o;
}