Unity Shader 漫反射/高光反射/卡通渲染 基础通用着色器代码
Unity Shader Diffuse / Specular / Cartoon 基础通用着色器代码分享我们知道Unity3D已经给出了几种不同的shader模板,比较常用的就是Standard Surface Shader和Unlit 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" 文件,通过一个变量保存。
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;漫反射是在获得了世界空间下的顶点法线和世界空间下的光源位置向量通过点乘得到的。我们把他放到片元着色器中逐像素计算。
float3 worldNormal = normalize(i.worldNormal); //world normal dir
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);// world light dir
float NdotL = dot(worldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
fixed3 diffuse = saturate(NdotL) * _DiffuseCol.rgb * _LightColor0.rgb ;高光反射是使用了BlinnPhong模型,计算出世界空间下的视角方向和光源方向相加去模后的半角向量后与世界空间下的顶点法线点乘。同样放在片元着色器中计算。
fixed3 worldPos = (i.worldPos); //注意 不要normalize,会出错
float3 worldNormal = normalize(i.worldNormal); //world normal dir
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);// world light dir
fixed3 worldViewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos); //world view dir
float3 halfDir = normalize( worldLightDir + worldViewDir );
float NdotH = max(0 , dot(halfDir , worldNormal));
float NdotL = dot(wrldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
fixed3 specular = pow((NdotH) , _Gloss) * _SpecularCol.rgb;最终加一起并且输出,就可以了。
fixed3 ads = diffuse + specular + ambient ; 接着说卡通渲染
相较于之前的光照模型,卡渲多的是梯度光照和边缘光。这里边缘光用的是视角方向和法线的点乘后进行的线性变化,具体是这篇博客。这里没加描边,大多数描边的做法是渲染背面,然后延法线扩展。
边缘光:
float rim = (1 - NdotV) * NdotL;
rim = smoothstep(_RimArea - _RimSmooth * 0.5, _RimArea + _RimSmooth * 0.5, rim);
float3 rimCol = rim * _RimCol;漫反射高光颜色和漫反射阴影颜色,这里选择可以直接控制颜色,用lerp来线性变换。
float4 diffCol = lerp(_DiffuseShadowCol ,_DiffuseHighLightCol, diff);
//fixed3 diffuse = saturate(diff)*_DiffuseCol.rgb * _LightColor0.rgb ;漫反射梯度和平滑度的控制:
float diff = floor(NdotL * _Step) / _Step ;下面贴上三个文件的完整代码:
漫反射:
Shader "Unlit/Unlit Diffuse Shader"
{
Properties
{
_DiffuseCol("Diffuse",COLOR) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
};
fixed4 _DiffuseCol;
fixed _Step;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal, unity_WorldToObject);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 worldNormal = normalize(i.worldNormal); //world normal dir
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);// world light dir
float NdotL = dot(worldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
fixed3 diffuse = saturate(NdotL) * _DiffuseCol.rgb * _LightColor0.rgb ;
fixed3 ad = diffuse + ambient;
fixed4 col = fixed4( diffuse, 1 );
return col;
}
ENDCG
}
}
}高光反射:
Shader "Unlit/Unlit Specular Shader"
{
Properties
{
_DiffuseCol("Diffuse",COLOR) = (1,1,1,1)
_SpecularCol("Specular" , COLOR) = (1,1,1,1)
_Gloss("Gloss",Range(8.0,256)) = 20
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
fixed4 _DiffuseCol;
fixed4 _SpecularCol;
fixed _Gloss;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal, unity_WorldToObject);
o.worldPos = mul(unity_ObjectToWorld , v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 worldPos = (i.worldPos); //注意 不要normalize,会出错
float3 worldNormal = normalize(i.worldNormal); //world normal dir
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);// world light dir
fixed3 worldViewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos); //world view dir
float3 halfDir = normalize( worldLightDir + worldViewDir );
float NdotH = max(0 , dot(halfDir , worldNormal));
float NdotL = dot(worldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
fixed3 diffuse = saturate(NdotL) * _DiffuseCol.rgb *_LightColor0.rgb ;
fixed3 specular = pow((NdotH) , _Gloss) * _SpecularCol.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 ads = diffuse + specular + ambient ;
fixed4 col = fixed4( ads, 1 );
return col;
}
ENDCG
}
}
}卡通渲染:
Shader "Unlit/Unlit Cartoon Shader"
{
Properties
{
_DiffuseHighLightCol("DiffuseHighLightCol",COLOR) = (1,1,1,1)
_DiffuseShadowCol("DiffuseShadowCol",COLOR) = (1,1,1,1)
_Step("Step" , Range(1,10)) = 3
_DiffuseSmooth("DiffSmooth" , Range(0,1)) = 0.5
_SpecularCol("Specular" , COLOR) = (1,1,1,1)
_Gloss("Gloss",Range(8.0,256)) = 20
_SpecSmooth("SpecSmooth" , Range(0,1)) = 0.5
_RimCol("Rim , COLOR) = (1,1,1,1)
_RimArea("RimArea" , Range(0,1)) = 0.5
_RimSmooth("RimSmooth" , Range(0,1)) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
fixed4 _DiffuseHighLightCol;
float4 _DiffuseShadowCol;
float _DiffuseSmooth;
fixed4 _SpecularCol;
fixed _Gloss;
float _SpecSmooth;
float4 _RimCol;
float _RimArea;
float _RimSmooth;
fixed _Step;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal, unity_WorldToObject);
o.worldPos = mul(unity_ObjectToWorld , v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 worldPos = (i.worldPos); //注意 不要normalize,会出错
float3 worldNormal = normalize(i.worldNormal); //world normal dir
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);// world light dir
fixed3 worldViewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos); //world view dir
float3 halfDir = normalize( worldLightDir + worldViewDir );
float NdotH = max(0 , dot(halfDir , worldNormal));
float NdotL = dot(worldNormal , worldLightDir); //NdotL : 法线方向点乘光照方向
float NdotV = dot(worldNormal , worldViewDir);
float diff = floor(NdotL * _Step) / _Step ;
diff = lerp(diff , NdotL , _DiffuseSmooth);
float4 diffCol = lerp(_DiffuseShadowCol ,_DiffuseHighLightCol, diff);
//fixed3 diffuse = saturate(diff)*_DiffuseCol.rgb * _LightColor0.rgb ;
fixed3 diffuse = diffCol.rgb * _LightColor0.rgb ;
//smoothstep(float min, float max, float t);
fixed3 specular =pow((NdotH) , _Gloss)* _SpecularCol.rgb;
specular = smoothstep(0.5 - _SpecSmooth * 0.5, 0.5 + _SpecSmooth * 0.5, specular);
float rim = (1 - NdotV) * NdotL;
rim = smoothstep(_RimArea - _RimSmooth * 0.5, _RimArea + _RimSmooth * 0.5, rim);
float3 rimCol = rim * _RimCol;
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 ads = diffuse + ambient + specular ;
fixed4 col = fixed4( ads + rimCol, 1 );
return col;
}
ENDCG
}
}
}谢谢!
2020.10.25
页:
[1]