找回密码
 立即注册
查看: 766|回复: 0

[笔记] Unity Shader 漫反射/高光反射/卡通渲染 基础通用着色器代码

[复制链接]
发表于 2021-4-25 09:28 | 显示全部楼层 |阅读模式
Unity Shader Diffuse / Specular / Cartoon 基础通用着色器代码分享
我们知道Unity3D已经给出了几种不同的shader模板,比较常用的就是Standard Surface ShaderUnlit Shader
前者是Unity自己把着色器代码封装起来,里面内嵌了SurfaceOutput结构体等内容来方便程序员去使用他。但是由于高度封装我们想要修改一些个性化效果就需要去查看源代码,这对于初学者来说是比较困难的 。
后者是Unity给出的标准顶点片元着色器代码,你可以在他的代码框架上加入各种东西来丰富shader效果。就像是Unity给了你一张白纸,你需要自己找笔来画画。
初学阶段我主要用顶点片元着色器来学习,而在材质编写中 最常用到着色器是去修改光照效果,即在基础的光照模型(漫反射和高光反射)上做文章,同时很多别的效果也都需要光照作为基础。由于我打字比较慢....最近在写shader的时候都要花一点时间重新在unlit shader上获取物体世界坐标,世界空间下的光源坐标,然后再写一遍漫反射的公式等这种很机械的工作(建议初学者还是多写几十遍加深印象),那样就之前突然有的点子在花几分钟敲完这些之后就没了(菜。我知道可以写一个CGInclude文件保存写好的光照模型然后调用,具体看女神的这篇,但是我更喜欢写代码的时候在一个文件看到各种量给我灵感(又菜又矫情。
所以综上,我用顶点片元shader写了三个通用着色器代码,并把文件分享出来,只有一些必要变量。用的时候下载下来拖进unity工程里,等于右键新建一个Unlit Diffuse Shader / Unlit Specular Shader / Unlit Cartoon Shader。哈哈这样我直接把笔找好了,你画就完了。
文件下载地址:
链接:https://pan.baidu.com/s/1hbzyNcG1y2NLFnJ8ak9OeQ
提取码:ybwx
同时我的代码也很适合初学者学习光照模型和简单的卡通渲染原理。我会把简单的原理写上,趁机再复习一遍。
先说光照模型,一张图解释基础光照。(图源闫令琪老师的GAMES101 Lecture8 这课无敌
最简单的光照就是场景环境光+漫反射光+高光反射,Unity对应的是这样的
Unity中环境光是通过直接获取得到的,先获取 "Lighting.cginc" 文件,通过一个变量保存。
  1. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
复制代码
漫反射是在获得了世界空间下的顶点法线和世界空间下的光源位置向量通过点乘得到的。我们把他放到片元着色器中逐像素计算。
  1. float3 worldNormal = normalize(i.worldNormal); //world normal dir
  2. fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  // world light dir
  3. float NdotL = dot(worldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
  4. fixed3 diffuse = saturate(NdotL) * _DiffuseCol.rgb * _LightColor0.rgb ;
复制代码
高光反射是使用了BlinnPhong模型,计算出世界空间下的视角方向和光源方向相加去模后的半角向量后与世界空间下的顶点法线点乘。同样放在片元着色器中计算。
  1. fixed3 worldPos = (i.worldPos); //注意 不要normalize,会出错
  2. float3 worldNormal = normalize(i.worldNormal); //world normal dir
  3. fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  // world light dir
  4. fixed3 worldViewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos); //world view dir
  5. float3 halfDir = normalize( worldLightDir + worldViewDir );
  6. float NdotH = max(0 , dot(halfDir , worldNormal));       
  7. float NdotL = dot(wrldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
  8. fixed3 specular = pow((NdotH) , _Gloss) * _SpecularCol.rgb;
复制代码
最终加一起并且输出,就可以了。
  1. fixed3 ads = diffuse + specular + ambient ;
复制代码
接着说卡通渲染
相较于之前的光照模型,卡渲多的是梯度光照和边缘光。这里边缘光用的是视角方向和法线的点乘后进行的线性变化,具体是这篇博客。这里没加描边,大多数描边的做法是渲染背面,然后延法线扩展。
边缘光:
  1. float rim = (1 - NdotV) * NdotL;
  2. rim = smoothstep(_RimArea - _RimSmooth * 0.5, _RimArea + _RimSmooth * 0.5, rim);
  3. float3 rimCol = rim * _RimCol;
复制代码
漫反射高光颜色和漫反射阴影颜色,这里选择可以直接控制颜色,用lerp来线性变换。
  1. float4 diffCol = lerp(_DiffuseShadowCol ,_DiffuseHighLightCol, diff);
  2. //fixed3 diffuse = saturate(diff)*_DiffuseCol.rgb * _LightColor0.rgb ;
复制代码
漫反射梯度和平滑度的控制:
  1. float diff = floor(NdotL * _Step) / _Step ;
复制代码
下面贴上三个文件的完整代码:
漫反射:
  1. Shader "Unlit/Unlit Diffuse Shader"
  2. {
  3.     Properties
  4.     {
  5.                 _DiffuseCol("Diffuse",COLOR) = (1,1,1,1)
  6.     }
  7.     SubShader
  8.     {
  9.         Tags { "RenderType"="Opaque" }
  10.         LOD 100
  11.         Pass
  12.         {
  13.             CGPROGRAM
  14.             #pragma vertex vert
  15.             #pragma fragment frag
  16.                         #include "Lighting.cginc"
  17.             struct appdata
  18.             {
  19.                 float4 vertex : POSITION;
  20.                 float3 normal : NORMAL;
  21.             };
  22.             struct v2f
  23.             {
  24.                 float4 vertex : SV_POSITION;
  25.                 float3 worldNormal : TEXCOORD0;
  26.             };
  27.                 fixed4 _DiffuseCol;
  28.                 fixed _Step;
  29.             v2f vert (appdata v)
  30.             {
  31.                 v2f o;
  32.                 o.vertex = UnityObjectToClipPos(v.vertex);
  33.                 o.worldNormal = mul(v.normal, unity_WorldToObject);
  34.                 return o;
  35.             }
  36.             fixed4 frag (v2f i) : SV_Target
  37.             {
  38.                 float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
  39.                 float3 worldNormal = normalize(i.worldNormal); //world normal dir
  40.                 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  // world light dir
  41.                 float NdotL = dot(worldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
  42.                 fixed3 diffuse = saturate(NdotL) * _DiffuseCol.rgb * _LightColor0.rgb ;
  43.                 fixed3 ad = diffuse + ambient;
  44.                 fixed4 col = fixed4( diffuse  , 1 );
  45.                 return col;
  46.             }
  47.             ENDCG
  48.         }
  49.     }
  50. }
复制代码
高光反射:
  1. Shader "Unlit/Unlit Specular Shader"
  2. {
  3.     Properties
  4.     {
  5.         _DiffuseCol("Diffuse",COLOR) = (1,1,1,1)
  6.         _SpecularCol("Specular" , COLOR) = (1,1,1,1)
  7.         _Gloss("Gloss",Range(8.0,256)) = 20
  8.     }
  9.     SubShader
  10.     {
  11.         Tags { "RenderType"="Opaque" }
  12.         LOD 100
  13.         Pass
  14.         {
  15.             CGPROGRAM
  16.             #pragma vertex vert
  17.             #pragma fragment frag
  18.                         #include "Lighting.cginc"
  19.             struct appdata
  20.             {
  21.                 float4 vertex : POSITION;
  22.                 float3 normal : NORMAL;
  23.             };
  24.             struct v2f
  25.             {
  26.                 float4 vertex : SV_POSITION;
  27.                 float3 worldNormal : TEXCOORD0;
  28.                 float3 worldPos : TEXCOORD1;
  29.             };
  30.                 fixed4 _DiffuseCol;
  31.                 fixed4 _SpecularCol;
  32.                 fixed _Gloss;
  33.             v2f vert (appdata v)
  34.             {
  35.                 v2f o;
  36.                 o.vertex = UnityObjectToClipPos(v.vertex);
  37.                 o.worldNormal = mul(v.normal, unity_WorldToObject);
  38.                 o.worldPos = mul(unity_ObjectToWorld , v.vertex);
  39.                 return o;
  40.             }
  41.             fixed4 frag (v2f i) : SV_Target
  42.             {
  43.                 fixed3 worldPos = (i.worldPos); //注意 不要normalize,会出错
  44.                 float3 worldNormal = normalize(i.worldNormal); //world normal dir
  45.                 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  // world light dir
  46.                 fixed3 worldViewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos); //world view dir
  47.                 float3 halfDir = normalize( worldLightDir + worldViewDir );
  48.                 float NdotH = max(0 , dot(halfDir , worldNormal));       
  49.                 float NdotL = dot(worldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
  50.                                
  51.                 fixed3 diffuse = saturate(NdotL) * _DiffuseCol.rgb *  _LightColor0.rgb ;
  52.                 fixed3 specular = pow((NdotH) , _Gloss) * _SpecularCol.rgb;
  53.                 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
  54.                 fixed3 ads = diffuse + specular + ambient ;
  55.                 fixed4 col = fixed4( ads  , 1 );
  56.                 return col;
  57.             }
  58.             ENDCG
  59.         }
  60.     }
  61. }
复制代码
卡通渲染:
  1. Shader "Unlit/Unlit Cartoon Shader"
  2. {
  3.     Properties
  4.     {
  5.                 _DiffuseHighLightCol("DiffuseHighLightCol",COLOR) = (1,1,1,1)
  6.                 _DiffuseShadowCol("DiffuseShadowCol",COLOR) = (1,1,1,1)
  7.                 _Step("Step" , Range(1,10)) = 3
  8.                 _DiffuseSmooth("DiffSmooth" , Range(0,1)) = 0.5
  9.                 _SpecularCol("Specular" , COLOR) = (1,1,1,1)
  10.                 _Gloss("Gloss",Range(8.0,256)) = 20
  11.                 _SpecSmooth("SpecSmooth" , Range(0,1)) = 0.5
  12.                 _RimCol("Rim , COLOR) = (1,1,1,1)
  13.                 _RimArea("RimArea" , Range(0,1)) = 0.5
  14.                 _RimSmooth("RimSmooth" , Range(0,1)) = 0.5
  15.     }
  16.     SubShader
  17.     {
  18.         Tags { "RenderType"="Opaque" }
  19.         LOD 100
  20.         Pass
  21.         {
  22.             CGPROGRAM
  23.             #pragma vertex vert
  24.             #pragma fragment frag
  25.             #include "UnityCG.cginc"
  26.             #include "Lighting.cginc"
  27.             struct appdata
  28.             {
  29.                 float4 vertex : POSITION;
  30.                 float3 normal : NORMAL;
  31.             };
  32.             struct v2f
  33.             {
  34.                 float4 vertex : SV_POSITION;
  35.                 float3 worldNormal : TEXCOORD0;
  36.                 float3 worldPos : TEXCOORD1;
  37.             };
  38.                         fixed4 _DiffuseHighLightCol;
  39.                         float4 _DiffuseShadowCol;
  40.                         float _DiffuseSmooth;
  41.                         fixed4 _SpecularCol;
  42.                         fixed _Gloss;
  43.                         float _SpecSmooth;
  44.                         float4 _RimCol;
  45.                         float _RimArea;
  46.                         float _RimSmooth;
  47.                         fixed _Step;
  48.             v2f vert (appdata v)
  49.             {
  50.                 v2f o;
  51.                 o.vertex = UnityObjectToClipPos(v.vertex);
  52.                 o.worldNormal = mul(v.normal, unity_WorldToObject);
  53.                 o.worldPos = mul(unity_ObjectToWorld , v.vertex);
  54.                 return o;
  55.             }
  56.             fixed4 frag (v2f i) : SV_Target
  57.             {
  58.                                 fixed3 worldPos = (i.worldPos); //注意 不要normalize,会出错
  59.                                 float3 worldNormal = normalize(i.worldNormal); //world normal dir
  60.                                 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  // world light dir
  61.                                 fixed3 worldViewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos); //world view dir
  62.                                 float3 halfDir = normalize( worldLightDir + worldViewDir );
  63.                                 float NdotH = max(0 , dot(halfDir , worldNormal));       
  64.                                 float NdotL = dot(worldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
  65.                                 float NdotV = dot(worldNormal , worldViewDir);
  66.                        
  67.                                 float diff = floor(NdotL * _Step) / _Step ;
  68.                                 diff = lerp(diff , NdotL , _DiffuseSmooth);
  69.                                 float4 diffCol = lerp(_DiffuseShadowCol ,_DiffuseHighLightCol, diff);
  70.                                 //fixed3 diffuse = saturate(diff)*_DiffuseCol.rgb * _LightColor0.rgb ;
  71.                                 fixed3 diffuse = diffCol.rgb * _LightColor0.rgb ;
  72.                                 //smoothstep(float min, float max, float t);
  73.                                 fixed3 specular =  pow((NdotH) , _Gloss)* _SpecularCol.rgb;
  74.                                 specular = smoothstep(0.5 - _SpecSmooth * 0.5, 0.5 + _SpecSmooth * 0.5, specular);
  75.                                 float rim = (1 - NdotV) * NdotL;
  76.                                 rim = smoothstep(_RimArea - _RimSmooth * 0.5, _RimArea + _RimSmooth * 0.5, rim);
  77.                                 float3 rimCol = rim * _RimCol;
  78.                                 float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
  79.                                 fixed3 ads = diffuse + ambient + specular ;
  80.                                 fixed4 col = fixed4( ads + rimCol  , 1 );
  81.                                 return col;
  82.             }
  83.             ENDCG
  84.         }
  85.     }
  86. }
复制代码
谢谢!
2020.10.25

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-23 11:51 , Processed in 0.091628 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表