永远相信美好的事情即将发生 😊!

Unity Shader 入门精要(冯乐乐著)学习笔记(16)——Unity的表面着色器

学习 Mavis 46℃ 0评论

unity中表面着色器实际是对顶点/片元着色器的抽象

Shader "Unity Shaders Book/Chapter 17/Bumped Diffuse" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BumpMap ("Normalmap", 2D) = "bump" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 300
		CGPROGRAM
		#pragma surface surf Lambert
		#pragma target 3.0
		sampler2D _MainTex;
		sampler2D _BumpMap;
		fixed4 _Color;
		struct Input {
			float2 uv_MainTex;//必须以uv开头
			float2 uv_BumpMap;
		};
		void surf (Input IN, inout SurfaceOutput o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = tex.rgb * _Color.rgb;
			o.Alpha = tex.a * _Color.a;
			o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
		}
		ENDCG
	} 
	FallBack "Legacy Shaders/Diffuse"
}

效果:

表面着色器重要的是两个结构体和编译指令。

编译指令

指明表面着色器使用的表面函数和光照函数,并设置一些可选参数:

#pragma surface surfaceFunction lightmode [optionalparams]

表面函数:

void surf (Input IN , inout SurfaceOutput o);

void surf (Input IN , inout SurfaceOutputStandard o);

void surf (Input IN , inout SurfaceOutputStandardSpecular o);

光照函数:

光照函数会使用表面函数中设置的各种表面属性,来应用某些光照模型,进而模拟物体表面的光照模型,使用内置的也可以自己定义。

还有一些其他可选参数:自定义的的修改函数、阴影、透明度混合和透明度测试、光照、控制代码的生成。

两个结构体

表面着色器最多自定义4种关键的函数:表面函数(用于设置各种表面性质,如反射率、法线等)、光照函数(定义表面使用的光照模型)、顶点修改函数(修改顶点或传递顶点属性)、最后的颜色修改函数(对最后的颜色进行修改)。

函数之间的信息传递通过结构体:表面函数的输入结构体Input,存储了表面属性的结构体SurfaceOutput,还有SurfaceOutputStandard 、SurfaceOutputStandardSpecular 。

input结构体:

SurfaceOutput结构体:Input结构体提供的数据用来计算表面属性然后存储到SurfaceOutput结构体中。作为表面函数的输出,随后作为光照函数的输入来计算各种光照。该类结构体变量不可增加也不会减少,没有赋值 的就用默认值:

表面着色器实例分析

实现效果:对模型进行膨胀,在顶点修改函数中沿着法线方向扩张顶点位置。

shader代码:

Shader "Unity Shaders Book/Chapter 17/Normal Extrusion" {
	Properties {
		_ColorTint ("Color Tint", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BumpMap ("Normalmap", 2D) = "bump" {}
		_Amount ("Extrusion Amount", Range(-0.5, 0.5)) = 0.1
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 300
		CGPROGRAM
		// surf - which surface function.
		// CustomLambert - which lighting model to use.
		// vertex:myvert - use custom vertex modification function.
		// finalcolor:mycolor - use custom final color modification function.
		// addshadow - generate a shadow caster pass. Because we modify the vertex position, the shder needs special shadows handling.
		// exclude_path:deferred/exclude_path:prepas - do not generate passes for deferred/legacy deferred rendering path.
		// nometa - do not generate a “meta” pass (that’s used by lightmapping & dynamic global illumination to extract surface information).
		#pragma surface surf CustomLambert vertex:myvert finalcolor:mycolor addshadow exclude_path:deferred exclude_path:prepass nometa
		#pragma target 3.0
		fixed4 _ColorTint;
		sampler2D _MainTex;
		sampler2D _BumpMap;
		half _Amount;
		//表面函数输入结构体
		struct Input {
			float2 uv_MainTex;
			float2 uv_BumpMap;
		};
		//自定义顶点修改函数
		void myvert (inout appdata_full v) {
			v.vertex.xyz += v.normal * _Amount;//对顶点位置按照法线方向进行膨胀
		}
		//自定义表面函数
		void surf (Input IN, inout SurfaceOutput o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = tex.rgb;//使用主纹理设置反射率
			o.Alpha = tex.a;
			o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));//使用法线纹理设置表面法线方向
		}
		//自定义光照函数
		half4 LightingCustomLambert (SurfaceOutput s, half3 lightDir, half atten) {
			half NdotL = dot(s.Normal, lightDir);
			half4 c;
			c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);//兰伯特漫反射光照模型
			c.a = s.Alpha;
			return c;
		}
		//自定义最后的颜色修改函数
		void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) {
			color *= _ColorTint;
		}
		ENDCG
	}
	FallBack "Legacy Shaders/Diffuse"
}

效果:

然后可以点击show generated code来查看表面着色器实际生成的shader代码:

表面着色器的缺点

性能较差且自定义程度低,优化程度不够。

 


Mavis , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Unity Shader 入门精要(冯乐乐著)学习笔记(16)——Unity的表面着色器
喜欢 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址