自定义光照模型
在这之前首先来了解一下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