ainatipen 发表于 2022-6-29 11:47

Unity Shader全息投影在内置渲染管线和URP中的使用

前言: 背景介绍


全息投影是一个让人感觉非常酷炫的科幻特效,在众多科幻电影和科幻背景的游戏中都能发现该特效的使用。例如《阿凡达》,《地平线·黎明时分》等等就使用了该特效,增强了科幻感,让观众和玩家领略到了科幻之美。作为现代游戏开发者,掌握全息投影技术就变得至关重要。本文将从0开始,介绍两个全息投影特效分别应对Unity的内置渲染管线和URP两种不同渲染管线。
第一节 , 会简述全息投影的整体思路,并且对关键步骤做出解释。
第二节 , 实际开发CG语言下的特效,并且对CG的API做出一定解释。
第三节, 实际开发URP下的特效,并且对URP管线流程和HLSL做出一定解释。
第四节 , 关于全息投影的优化以及如何制作投射灯台做出介绍并且贴出参考链接

文章主体代码参考:


副参考:

<hr/>一. 整体思路和关键步骤解释

使用主贴图和法线图进行操作。主贴图负责提供模型表面的颜色信息,灰度信息等,法线图提供法线信息做出菲涅尔效果。边缘光和主贴图采样颜色一起构成Emission,且Alpha = Emission。



流程简介图

全息投影技术重点在于模型要产生不依赖与模型uv的纹理效果。计算顶点在屏幕中的位置是个不错的选择。使用ComputeScreenPos(i.vertex);函数可以获得顶点在屏幕中的坐标,以这个坐标来采样,产生的效果就相当于当前的屏幕盖了一张纹理图。同时,如果想要产生随视角变化的纹理,可以考虑使用模型在世界空间中的坐标的两个值来做uv坐标,这同样是脱离模型uv的。                                                                                                                                                         ————《使用shader产生全息投影的效果》晨蓝fk二. 内置渲染管线使用表面着色器

Shader "Custom/Hologram" {
    Properties {
            _MainTex ("Main Tex ", 2D) = "white" {}//主纹理
            _BumpMap("Bumpmap",2D)="bump"{}    //法线贴图
            _RimColor ("Rim Color", Color) = (0.26,0.7,1.0,0.0)//边缘光颜色
            _RimPower ("Rim Power", Range(0.1,8.0)) = 3.0//边缘光放大倍数
            _ClipPower("Clip Power",Range(0.0,301.0))=200.0 //分割强度参见surfaceshader examples 中Slices via World Space Position例子,值越大条纹越细
            _Brightness("Brightness",Range(0.0,3.0))=1.5//光强
            _DiffuseAmount("Diffuse Amount",Range(0.0,1.0))=0.0//渐变比例值,看后面lerp函数就明白
            _Speed ("Speed",Range(0,1))=1
      }
    SubShader
    {
      Tags
      {
         "RenderType"="Transparent"
         "Queue"="Transparent"
         "IgnoreProjector"="True"
      }
            


      CGPROGRAM
      
      #pragma surface surf Lambert alpha noambient nolightmap nodirlightmap novertexlights
      struct Input{
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float3 viewDir;
            float3 worldPos;   
            float4 screenPos;      
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      float4 _RimColor;
      float _RimPower;
      float _ClipPower;
      float _Brightness;
      float _DiffuseAmount;
      float _Speed;
      void surf(Input IN,inout SurfaceOutput o)
      {
         //screenPos是一个三维点,但是用齐次坐标的形式表示出来就是(x,y,z,w)
         //根据齐次坐标的性质。(x,y,z,w)的齐次坐标对应三维点(x/w,y/w,z/w)
         float2 screenUV = IN.screenPos.xy / IN.screenPos.w; //实际的屏幕xy的UV值。

         //frac是取小数的函数,如1.23取出来就是0.23
         //clip 如果输入向量中的任何元素小于0,则丢弃当前像素。
         screenUV.y += abs(_SinTime.y * _Speed);//设置流动效果
         if( _ClipPower <= 300.0f)
             clip( frac(screenUV.y * _ClipPower ) - 0.5);

         half4 basecol=tex2D(_MainTex,IN.uv_MainTex);
         //灰度值
         half3 graycol=dot(basecol.rgb,float3(0.3,0.59,0.11));

         o.Albedo = lerp(graycol,basecol,_DiffuseAmount);
         //法线贴图
         o.Normal = UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap));
         //边缘光
         half rim = 1.0-saturate(dot(normalize(IN.viewDir),o.Normal));
         o.Emission = lerp(_RimColor.rgb * pow (rim, _RimPower) * _Brightness,basecol,_DiffuseAmount);
         o.Alpha = o.Emission;
      }
      ENDCG
    }   
    FallBack "Diffuse"
}



简单的效果

三. URP下使用全息投影特效

接入URP,最关键的是CG转HLSL。在HLSL中,有些CG的API和宏不能在HLSL中继续使用,因此就要手动翻译。这里贴出一个网站,帮助大家更好的转成HLSL:
如果不想些HLSL,也可以考虑使用ASE或者Unity自带的Shader Graph来制作URP下的shader,这里我推荐:



大哥已经不做这块的内容,享受生活去了

国内也有大哥根据此教程做出了效果:
整体思路大同小异,都是用菲涅尔效果做边缘光,然后动态纹理,剔除对应像素,然后给原图乘以一个颜色。具体实现细节就不讲了。

四. 灯台效果

这块笔者还没做出来(不会建模),参考链接直接摆出来:
等我做完,会去更新这篇文章(yinggai
页: [1]
查看完整版本: Unity Shader全息投影在内置渲染管线和URP中的使用