Unity3D游戏开发从零单排(十) - 进击的Shader续

自定义光照模型

在这之前首先来了解一下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


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。