Unity CG着色器实战

卡通风格

  1. 先一个Pass只渲染背面,黑色,沿法线膨胀,做轮廓线效果;

  2. 正式渲染Pass,漫反射采样一个逐渐变暗的纹理,做出硬边明暗。高光反射和一个阈值比较,大于则直接显示高光颜色。

    Shader "My/Toon"
    {
    Properties
    {
    _Color ("Color", Color) =(1,1,1,1)
    _SpeColor("SpeColor",Color)=(1,1,1,1)
    _SpecThreshold("SpecThreshold",Range(0.1,1))=1
    _MainTex("MainTex",2D)="white"{}
    _ShadeTex("ShadeTex",2D)="white"{}
    _OutlineWidth("OutlineWidth",Range(0.001,0.1))=0.01
    _OutlineCol("OutlineColor",Color)=(0,0,0,1)
    [Enum(UnityEngine.Rendering.CullMode)]_Cull("Cull",Float)=0
    }
    SubShader
    {
    Tags { "RenderType"="TransparentCutout""Queue"="AlphaTest"}
    LOD 100
    Pass{
    Name "Outline"
    Cull Front

    复制代码
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #include "UnityCG.cginc"
             struct v2f
             {
                 float4 vertex: SV_POSITION;
             };
             float _OutlineWidth;
             fixed4 _OutlineCol;
             v2f vert(appdata_base v){
                 v2f o;
                 v.vertex.xyz+= normalize(v.normal)* _OutlineWidth;
                 o.vertex= UnityObjectToClipPos(v.vertex);
                 return o;
             }
             fixed4 frag(v2f i): SV_Target{
                 return _OutlineCol;
             }
             ENDCG
         }
         Pass
         {
             Name "Main"
             Tags{"LightMode" = "ForwardBase"}
             Cull [_Cull]
             Blend SrcAlpha OneMinusSrcAlpha
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #pragma multi_compile_fwdbase
             #include "UnityCG.cginc"
             #include "Lighting.cginc"
             #include "AutoLight.cginc"
             struct appdata
             {
                 float4 vertex : POSITION;
                 float3 normal : NORMAL;
                 float2 uv:TEXCOORD0;
             };
    
             struct v2f
             {
                 float4 pos : SV_POSITION;
                 float2 uv:TEXCOORD0;
                 float3 worldPos: TEXCOORD1;
                 float3 worldNormal: TEXCOORD2;
                 float3 viewDir: TEXCOORD3;
                 float3 lightDir: TEXCOORD4;
                 SHADOW_COORDS(5)
             };
             fixed4 _Color;
             fixed4 _SpeColor;
             float _SpecThreshold;
             sampler2D _MainTex;
             float4 _MainTex_ST;
             sampler2D _ShadeTex;
             v2f vert (appdata v)
             {
                 v2f o;
                 o.pos = UnityObjectToClipPos(v.vertex);
                 o.worldNormal=normalize(UnityObjectToWorldNormal(v.normal));
                 
                 o.lightDir=-normalize(_WorldSpaceLightPos0.xyz);
                 o.worldPos=mul(unity_ObjectToWorld,v.vertex);
                 o.viewDir=normalize(_WorldSpaceCameraPos-o.worldPos);
    
                 TRANSFER_SHADOW(o);
                 o.uv=v.uv;
                 return o;
             }
    
             fixed4 frag (v2f i) : SV_Target
             {
                 fixed4 texCol=tex2D(_MainTex,i.uv);
                 //漫反射
                 float3 lightPos=normalize(_WorldSpaceLightPos0.xyz);
                 fixed diff=dot(i.worldNormal,lightPos)*0.5+0.5;
                 UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
                 diff*=atten;
                 fixed4 shadeCol=tex2D(_ShadeTex,float2(diff,0));
                 //高光
                 float3 reflDir=reflect(i.lightDir,i.worldNormal);
                 float spec=step(_SpecThreshold ,dot(reflDir,i.viewDir));
                 fixed3 color=_Color.rgb* texCol.rgb*(UNITY_LIGHTMODEL_AMBIENT+ shadeCol)
                 +_SpeColor.rgb* spec;
                 return fixed4(color,texCol.a);
                 //return UNITY_LIGHTMODEL_AMBIENT;
             }
             ENDCG
         }
     }
     Fallback "Diffuse"

    }

脸的下部有点黑,可以加一个漫反射明暗到渐变贴图u的系数,控制漫反射变黑的阈值。原神的效果比这个更亮。这个有点纸模感。

消融效果

  1. uv采样柏林噪声贴图,小于阈值则clip()不渲染;
复制代码
Shader "My/Melt"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _NoiseTex("NoiseTex",2D)=""{}
        _EdgeTex("EdgeTex",2D)=""{}
        _EdgeWidth("EdgeWidth",Range(0,1))=0.01
        _MeltThre("MeltThreshold",Range(0,1))=0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _NoiseTex;
            sampler2D _EdgeTex;
            fixed _EdgeWidth;
            fixed _MeltThre;
            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed no= tex2D(_NoiseTex,i.uv);
                clip(no- _MeltThre);
                //噪声离阈值的差小于edge宽度时,开始显示边缘;
                //把差值相对于edge宽度的比例转换到0-1,用于采样贴图
                fixed edgeU=1- smoothstep(0,_EdgeWidth,no- _MeltThre);
                fixed4 edgeCol= tex2D(_EdgeTex,fixed2(edgeU,0.5));
                fixed4 col = tex2D(_MainTex, i.uv);
                return lerp(col, edgeCol,edgeU);
            }
            ENDCG
        }
    }
}

水面

复制代码
Shader "My/Water"
{
    Properties{
        _MainTex("MainTex",2D)=""{}
        _NormalTex("NormalTex",2D)=""{}
        _Cube("Cubemap",Cube)=""{}
        _Distortion("Distortion",Range(0,1))=0
        _XSpeed("XSpeed",Range(-0.1,0.1))=0.01
        _YSpeed("YSpeed",Range(-0.1,0.1))=0.01
        _Fresnel("Fresnel",Range(0,1))=1
    }
    SubShader{
        Tags{"RenderType"="Opaque""Queue"="Transparent"}
        GrabPass{}
        Pass{
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _NormalTex;
            float4 _NormalTex_ST;
            samplerCUBE _Cube;
            sampler2D _GrabTexture;//GrabPass捕获的图像,默认名
            float _Distortion;
            fixed _XSpeed;
            fixed _YSpeed;
            float _Fresnel;
            struct v2f
            {
                float4 pos:SV_POSITION;
                float4 grabPos:TEXCOORD0; 
                float4 uv:TEXCOORD1;
                float4 TtoW0: TEXCOORD3;
                float4 TtoW1: TEXCOORD4;
                float4 TtoW2: TEXCOORD5;
            };
            v2f vert(appdata_full v){
                v2f o;
                o.pos= UnityObjectToClipPos(v.vertex);
                o.grabPos= ComputeScreenPos(o.pos);
                o.uv.xy=v.texcoord.xy* _MainTex_ST.xy+ _MainTex_ST.zw;
                o.uv.zw=v.texcoord.xy* _NormalTex_ST.xy+ _NormalTex_ST.zw;
                float3 worldNormal= UnityObjectToWorldNormal(v.normal);
                fixed3 worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
                //
                float3 worldTan= UnityObjectToWorldDir(v.tangent);
                float3 worldBinormal= cross(normalize(worldTan),normalize(worldNormal))*v.tangent.w;
                
                o.TtoW0=float4(worldTan.x,worldBinormal.x,worldNormal.x,worldPos.x);
                o.TtoW1=float4(worldTan.y,worldBinormal.y,worldNormal.y,worldPos.y);
                o.TtoW2=float4(worldTan.z,worldBinormal.z,worldNormal.z,worldPos.z);
                return o;
            }
            fixed4 frag(v2f i):SV_Target{
                float3 worldPos=float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                //float4 normal= tex2D(_NormalTex,i.uv.zw);
                ////法线贴图的法线在切线空间
                //float3 tanNormal=UnpackNormal(normal);
                float2 speed= _Time.y*float2( _XSpeed,_YSpeed);
                float4 packNormal= tex2D(_NormalTex,i.uv.zw+ speed);
                fixed3 bump1= UnpackNormal(packNormal).rgb;
                packNormal= tex2D(_NormalTex,i.uv.zw- speed);
                fixed3 bump2= UnpackNormal(packNormal).rgb;
                fixed3 bump= normalize( bump1+bump2);
                ////法线贴图的法线在世界
                float3 worldNormal =
                float3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump));

                fixed3 albedo = tex2D(_MainTex, i.uv.xy);
                float3 refl= reflect(-viewDir,worldNormal);
                //兰伯特
                //fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(worldNormal, normalize(_WorldSpaceLightPos0.xyz)));
                //半角向量
                //float3 halfA = normalize(normalize(viewDir) + normalize(_WorldSpaceLightPos0.xyz));
                //高光反射
                //fixed3 specularColor = _LightColor0.rgb* pow(max(0, dot(worldNormal, halfA)), _SpecularNum);
                fixed4 reflCol= texCUBE(_Cube,refl)*tex2D(_MainTex,i.uv);
                float2 offset= bump.xy*_Distortion;
                i.grabPos.xy= offset*i.grabPos.z+ i.grabPos.xy;
                fixed2 screenUV=(i.grabPos.xy+offset)/i.grabPos.w;
                fixed4 grabCol= tex2D(_GrabTexture,screenUV);
                fixed fresnel= _Fresnel+(1- _Fresnel)*
                pow((1-dot(normalize(viewDir),normalize(worldNormal))),5);
                float4 color=reflCol*(1-fresnel)+grabCol*fresnel;
                return color;
            }
            ENDCG
        }
    }
}
相关推荐
废嘉在线抓狂.3 小时前
TimeLine如何自定义轨道
unity·c#·对话系统
ellis197018 小时前
Unity资源管理框架Addressables[六] 内存管理
unity
不吃鱼的猫74819 小时前
【从零开始学 OpenGL:现代图形渲染实战】第03篇-深入着色器与GLSL
图形渲染·着色器
派葛穆19 小时前
Unity-鼠标悬停改变物体层级
unity·游戏引擎
小贺儿开发1 天前
Unity3D 爆炸图案例演示
unity·产品·urp·机械拆装·爆炸图·零件·效果设计
Yasin Chen2 天前
Unity TMP_SDF 分析(二)数据来源2
unity·游戏引擎
Mao_Hui2 天前
Unity3d实时读取Modbus RTU数据
开发语言·嵌入式硬件·unity·c#
心疼你的一切2 天前
【Unity-MCP完全指南:从零开始构建AI游戏开发助手】
人工智能·unity·ai·游戏引擎·aigc·mcp
示申○言舌2 天前
基于知识库(RAG)系统打造由大模型(LLM)驱动NPC游戏的技术设想
游戏·unity·大模型·知识库·rag·智能npc·npc记忆