unity-shader-思路与技巧


title: unity-shader-思路与技巧
categories: Unity3d-Shader
tags: [unity, shader, 思路, 技巧, 奇技淫巧]
date: 2019-05-01 00:49:32
comments: false

目的是为了想实现效果时, 快速找到实现的方案.
主要用 shader graph 实现, 可视化会更容易于理解, 部分会结合代码.


与顶点位置相关

unity-shader-思路与技巧

  • 使用 模型空间 顶点位置, 乘以一个系数 spread 来控制顶点位置的缩放, 加上一个系数 position 来控制顶点位置的偏移

    unity-shader-思路与技巧


顶点法线 Dot

一般都是用 dot(normal, vector3) 来获取夹角余弦值

以 积雪 效果为例

unity-shader-思路与技巧

  • snow direction 与 法线 点乘, 主要控制哪个方向的面需要做效果
  • snow depth 和 step 控制夹角多少以内需要做效果
  • 再结合 fresnel 做平滑过渡.

判断 相交

  • 神奇的深度图:复杂的效果,不复杂的原理 - http://www.php361.com/index.php?c=index&a=view&id=5257

  • 测试: 关键字: force field. 红色部分为 代码 实现, 黄色部分为 shader graph 实现, 两者逻辑一样.

    unity-shader-思路与技巧

  • 相关代码

    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;
    }
    
  • 原理

    1. 判断相交. 用 自己的深度 与 场景的深度(也就是深度图的值) 比较. ( 因为是自身是 transparent, 所以不会渲染到深度图中)
    2. 圆环 rim. NdotV.
  • 应用场景

    • 水面与物体交界处
    • 能量场

另外: shader graph 中的实现

unity-shader-思路与技巧


切片效果

主要利用函数 frac
y=xfloor(x) y=x-floor(x)
unity-shader-思路与技巧

unity-shader-思路与技巧

  • 利用模型顶点的 y轴分量, slice value1fraction函数 控制切片细分程度, slice value2 控制细分宽度

    unity-shader-思路与技巧


uv流动 的几种方式

unity-shader-思路与技巧

  1. 基于 纹理uv. 模型顶点的uv值. (看起来有点乱, 因为 uv值 的是在 uv展开 的时候决定的)

    unity-shader-思路与技巧

  2. 基于 顶点位置, 又分为 切线,对象,世界,观察 四个空间的

    • 观察空间下

      unity-shader-思路与技巧

  3. 基于 屏幕空间的位置. 简答的理解就是屏幕的 左下角是(0,0), 右下角是(1, 1), 顶点都位于这个区间内

    unity-shader-思路与技巧

基于 位置 (2和3) 的看起来贴图是贴在屏幕上的.


判断 边缘, 平滑, 宽度

smoothstep 函数用于 平滑, 宽度 计算

step 函数用于判断

  1. 基于 纹理uv

    unity-shader-思路与技巧

    • 效果

      unity-shader-思路与技巧

    • 代码

      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;
      }
      
  2. 基于 顶点位置

    unity-shader-思路与技巧

    • split value 控制clip的值, 及边缘的 边界值2, edge spread 控制边缘的宽度, edge spread 越小就越接近 边界值2, 也就是越窄

      上半部分计算宽度及渐变, 下半部分计算clip

      unity-shader-思路与技巧

    • 代码

      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;
      }
      

理解 边缘 效果思路

unity-shader-思路与技巧

  • step

    unity-shader-思路与技巧

  • smoothstep

    unity-shader-思路与技巧


程序算法节点

unity-shader-思路与技巧

可以参考文档里面的算法实现, 搬运代码时方便 - https://docs.unity3d.com/Packages/[email protected]/manual/Procedural-Nodes.html


顶点沿法线偏移

可用于 描边 pass, 只显示背面

unity-shader-思路与技巧

代码

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;
}