shader入门精要读书笔记46 表面着色器

一、简述

表面着色器的CG代码是直接且必须写在SubShader块中,Unity会为我们在背后生成多个Pass,可以在SubShader一开始处使用Tags来设置表面着色器使用的标签。

一个表面着色器中最重要的就是两个结构体以及它的编译指令。两个结构体是表面着色器中不同函数之间信息传递的桥梁,编译指令是我们和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;
			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"
}

二、编译指令

  1. 指明该编译指令是用于定义表面着色器的
#pragma surface surfaceFunction lightMode [Optionalparams]

上面例子中那句

#pragma surface surf Lambert			//使用的表面函数surf、光照模型Lambert
  1. 表面函数

表面属性包括反射率、光滑度、透明度等值,而编译指令中的surfaceFunction就用于定义这些属性。
surfaceFunction就是名为surf的函数,

void surf (Input IN,inout SurfaceOutput o)
void surf (Input IN,inout SurfaceOutputStandard o)
void surf (Input IN,inout SurfaceOutputStandardSpecular o)

上面这三个SurfaceOutput、SurfaceOutputStandard 、SurfaceOutputStandardSpecular都是Untiy内置的结构体,他们需要配合不同的光照模型来使用,

Input IN 是输入结构体,用来设置各种表面属性的,再把这些属性存储在输出结构体SurfaceOutput、SurfaceOutputStandard 、SurfaceOutputStandardSpecular中,再传递给光照函数计算光照结果。

  1. 光照函数

光照函数会使用表面函数中设置的各种表面属性,进行应用某些光照模型,模拟物体表面的光照效果。

Unity中有内置了基于物理的光照模型函数 Standard 和 StandardSpecular ,以及简单的非基于物理的光照模型函数 Lambert 和 BlinnPhong 。比如上面那个例子中,我们使用的 Lambert 。

我们也可以定义自己的光照函数,例如使用下面的函数定义用于前向渲染中的光照函数:

half4 Lighting<Name> (SurfaceOutput s,half3 lightDir,half viewDir,half atten);
  1. 其他可选参数

我们可以自定义修改函数,顶点修改函数、最后颜色修改函数。
阴影 —addshadow,—fullforwardshadows,—noshadow,
透明度混合、透明度测试,alpha、alphatest
光照,
控制代码生成
等等。。

三、两个结构体

P332

  1. Input结构体,包含很多表面属性的数据来源,内置很多变量名,使用这些变量名字,告诉Untiy需要时用的数据信息。
    我们不需要自己计算上述变量,只需要在结构体中定义(严格定义名字),然后我们直接在表面函数中使用即可。

  2. SurfaceOutput

作为表面函数的输出,随后作为光照函数输入计算光照。

P333

如果我们使用的 非物理光照模型 ,那通常 使用SurfaceOutput结构体。
使用 基于 物理的光照模型时, 通常使用 SurfaceOutputStandard 、SurfaceOutputStandardSpecular结构体。

四、实例

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
		
		#pragma surface surf CustomLambert vertex:myvert finalcolor:mycolor addshadow exclude_path:deferred exclude_path:prepass nometa
		//编译指令很多参数,addshadow 生成阴影投射Pass,
		//exclude_path:deferred exclude_path:prepass 告诉Unity不生成延迟渲染路径的相关Pass。
		//nometa 取消对提取元数据的Pass的生成。
		#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"
}

具体Unity背后怎么生成的Pass可见 P337-P341

表面着色器缺点 P341

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值