我们知道在surfaceShader中可以通过#pragma surface surf Standard 直接调用标准光照模型,Unity对surfaceShader封装了太多东西。本节参照NewSurfaceShader编译成的VFShader实现自己的VFSahder中的标准光照模型。
代码如下:
Shader "Unlit/NewUnlitShader"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Glossiness("Smoothness", Range(0,1)) = 0.5
_Metallic("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"
sampler2D _MainTex;
half _Glossiness;
half _Metallic;
fixed4 _Color;
struct v2f
{
float4 pos : SV_POSITION;
half3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
float2 uv:TEXCOORD0;
};
v2f vert (appdata_full v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;//世界空间的顶点坐标和法线
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = worldPos;
o.worldNormal = worldNormal;
o.uv = v.texcoord;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldPos = i.worldPos;
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//-------配置LightingStandard_GI()和LightingStandard()的第一个参数
SurfaceOutputStandard o = (SurfaceOutputStandard)0;
fixed4 col= tex2D(_MainTex, i.uv)*_Color;
o.Albedo = col.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Normal = i.worldNormal;
//---------------end
//---------------提取的光照部分
fixed4 c = 0;
//配置LightingStandard()的第三个参数
UnityGI gi;
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = _LightColor0.rgb;
gi.light.dir = lightDir;
// 配置LightingStandard_GI()的第二个参数
UnityGIInput giInput;
giInput.worldViewDir = worldViewDir;
LightingStandard_GI(o, giInput, gi);
c += LightingStandard(o, worldViewDir, gi);
//------------------------end
return c;
}
ENDCG
}
}
}
右侧是unity默认Standard材质,两者差异还是蛮大的,因为unity内置的standard Shader 在n多个pass里面针对不同情况分别做了包括forwardadd和延迟光照渲染等不同的处理,所以效果会比左侧单个pass实现的光照模型效果要好很多。不管哪个pass,调用的光照模型是一样的。
下面我们将提取的光照部分独立出来封装成自己的cginc文件:
StandardLight.cginc文件代码
#ifndef StandardLight
#define StandardLight
float4 GetLightModel(SurfaceOutputStandard o, float3 worldPos, fixed3 lightDir, fixed3 worldViewDir) {
//---------------提取的光照部分
fixed4 c = 0;
//配置LightingStandard()的第三个参数
UnityGI gi;
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = _LightColor0.rgb;
gi.light.dir = lightDir;
// 配置LightingStandard_GI()的第二个参数
UnityGIInput giInput;
giInput.worldViewDir = worldViewDir;
LightingStandard_GI(o, giInput, gi);
c += LightingStandard(o, worldViewDir, gi);
//------------------------end
return c;
}
Shader部分代码:
Shader "Unlit/NewUnlitShader"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Glossiness("Smoothness", Range(0,1)) = 0.5
_Metallic("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"
#include"StandardLight.cginc"
sampler2D _MainTex;
half _Glossiness;
half _Metallic;
fixed4 _Color;
struct v2f
{
float4 pos : SV_POSITION;
half3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
float2 uv:TEXCOORD0;
};
v2f vert (appdata_full v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;//世界空间的顶点坐标和法线
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = worldPos;
o.worldNormal = worldNormal;
o.uv = v.texcoord;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldPos = i.worldPos;
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//-------配置LightingStandard_GI()和LightingStandard()的第一个参数
SurfaceOutputStandard o = (SurfaceOutputStandard)0;
fixed4 col = tex2D(_MainTex, i.uv)*_Color;
o.Albedo = col.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Normal = i.worldNormal;
//---------------end
return GetLightModel(o, worldPos, lightDir, worldViewDir);
}
ENDCG
}
}
}
如果不希望借用SurfaceOutputStandard结构体和LightingStandard()来实现金属光泽和光滑度等效果,可直接调UNITY_BRDF_PBS()方法,过滤掉LightingStandard()中对金属光泽和光滑度等参数的封装和自发光等效果的实现。