今天我们来学习一下怎么在Unity里实现自定义Phong光照模型
什么是Phong光照?
Phong光照=环境光+漫反射光+镜面高光
环境光和漫反射光比较容易计算,那镜面高光要如何计算呢?
镜面高光其实可以通过利用入射光方向(即光照方向的反方向)和法线计算出反射光方向,然后再通过反射光方向和观察视角方向计算出实际进入眼睛的光照强度,然后再做一个光滑系数的指数运算就可以得到镜面高光了。
反射光方向 = reflect(-lightDir, normal)
spec = dot(反射光方向, viewDir)
specular = pow(spec, specPower)
下面是在Unity的表面着色器中实现的自定义Phong光照模型:
Shader "Custom/Phong" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_SpecPower ("Specular Power", Range(1.0, 20.0)) = 1.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Phong
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
float4 _Color;
float _SpecPower;
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
inline fixed4 LightingPhong(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
//计算环境光
fixed3 ambient = s.Albedo * UNITY_LIGHTMODEL_AMBIENT;
//计算漫反射光
float diff = max(0, dot(s.Normal, lightDir));
fixed3 finalDiffuse = s.Albedo * _LightColor0.rgb * diff * atten * viewDir;
//计算镜面高光
fixed3 r = normalize(reflect(-lightDir, s.Normal));
float spec = pow(max(0, dot(r, viewDir)), _SpecPower);
fixed3 finalSpec = _LightColor0.rgb * spec * atten;
fixed4 c;
c.rgb = ambient + finalDiffuse + finalSpec;
c.a = 1.0;
return c;
}
ENDCG
}
FallBack "Diffuse"
}
如果是顶点片段着色器又该如何实现Phong光照呢?其实差不多,原理都一样,如下:
Shader "Unlit/Phong_frag"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_SpecularPower ("Specular Power", Range(1, 100)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
fixed3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
fixed3 normal : NORMAL;
float3 worldPos : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _SpecularPower;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.normal = normalize(UnityObjectToWorldNormal(v.normal));
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv) * _Color;
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
//计算环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * col.rgb;
//计算漫反射光
fixed diff = max(0, dot(lightDir, i.normal));
fixed3 finalDiffuse = col.rgb * _LightColor0.rgb * diff;
//计算镜面高光
fixed3 r = normalize(reflect(-lightDir, i.normal));
fixed spec = pow(max(0, dot(r, viewDir)), _SpecularPower);
fixed3 finalSpec = _LightColor0.rgb * spec;
col.rgb = ambient + finalDiffuse + finalSpec;
return col;
}
ENDCG
}
}
}
网友评论