自定义光照模型
在这之前首先来了解一下SurfaceOutput这个结构体,它是一个包含大多数描述一个物体表面渲染特征的结构,具体结构如下:
struct SurfaceOutput {
half3 Albedo;//纹理颜色
half3 Normal;//法线
half3 Emission;//自发光,不受照明的影响
half Specular;//高光指数
half Gloss;//光泽度
half Alpha;//Alpha通道
};
基本上所有的Shader函数要处理的就是这个结构体。
Unity自带的光照实现都定义在一些*.cginc文件中,要自定义光照模型,只要不用Unity自带的光照模型就可以了。
将下面这一行的语句的最后替换成对应的光照计算函数。
#pragma surface surf NoLight
无光照的材质Shader
Shader "Custom/NoLight" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf NoLight
sampler2D _MainTex;
inline float4 LightingNoLight(SurfaceOutput s, fixed3 lightDir, fixed3 atten)
{
float4 col;
col.rgb = s.Albedo;
col.a = s.Alpha;
return col;
}
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
自己实现一个diffuse
inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
float difLight = max(0, dot (s.Normal, lightDir));
float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
col.a = s.Alpha;
return col;
}
上图中左边是NoLight,右边是SimpleDiffuse。
同理可以自定义实现各种光照模型了,Lambert,Blinning,,,,,
补充一下光照函数的几种写法
half4 LightingName (SurfaceOutput s, half3 lightDir,half atten){}
这个函数用于不需要视角方向的情况下的Forward rendering。
half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten){}
这个函数用于需要视角方向的情况下的Forward rendering。
half4 LightingName_PrePass (SurfaceOutput s, half4 light){}
这个函数用于Deferred rendering。
用代码控制shader动态修改材质
对SimpleDiffuse稍微做一下修改,添加一个叠加的颜色。
Shader "Custom/SimpleDiffuse" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Color ("Main Color", Color) = (0,1,0,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
float4 _Color;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb * _Color.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
在模型(有MeshRenderer的)上面挂一个脚本,实现如下
MeshChanger
using UnityEngine;
using System.Collections;
public class MaterialChanger : MonoBehaviour {
float time = 0.0f;
float changeSpeed1 = 2.0f;
float changeSpeed2 = 5.0f;
Renderer render;
// Use this for initialization
void Start () {
render = transform.GetComponent<Renderer>();
}
// Update is called once per frame
void Update () {
float v1 = (Mathf.Cos(time * changeSpeed1) + 1.0f) * 0.5f;
float v2 = (Mathf.Sin(time * changeSpeed2)+ 1.0f) * 0.5f;
render.materials[0].SetColor("_Color", new Color(v1, v2, v2));
time += Time.deltaTime;
}
}
根据时间动态修改0号材质的_Color选项,结果就像这样
自行脑补中间过程。
使用渐变纹理来处理光照
首先要准备一张渐变纹理,原理就是通过计算当前位置的光照与法线的点积,索引到渐变图片上的像素值,最后将其和diffuse叠加。
Shader "Custom/RampTexture" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_RampTex ("Ramp Tex (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf RampLight
sampler2D _MainTex;
sampler2D _RampTex;
half4 LightingRampLight (SurfaceOutput s, half3 lightDir, half atten)
{
half NdotL = dot(s.Normal, lightDir);
float diff = NdotL * 0.5 + 0.5;
half3 ramp = tex2D(_RampTex, float2(diff, diff)).rgb;
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
渲染结果
没有纹理的情况(是大象不是小猪)
换一下索引贴图
得到结果如下(Toon Shading)
参考
Unity Shaders and Effects CookBook