unity 怎么编辑html控制颜色_unity片元着色器中获取屏幕坐标


混合模式

我们的着色器可以用来创建无光不透明的材料。可以改变颜色的alpha值,通常表示透明度,但目前没有效果。我们也可以将渲染队列设置为Transparent,但这只改变对象被绘制的顺序,对解决没有什么影响。


unity 怎么编辑html控制颜色_unity片元着色器中获取屏幕坐标_02


我们不需要编写一个单独的shader来支持透明的材质。通过一些工作,我们的Unlit shader可以同时支持不透明和透明渲染。

不透明和透明渲染之间的主要区别是,我们是要替换之前绘制的结果,还是与之前的结果结合以产生透明的效果。我们可以通过设置源颜色和目标颜色的混合模式来控制。这里source指的是现在绘制的内容,destination指的是之前绘制的内容以及结果的最终位置。为此添加两个着色器属性:_SrcBlend和_DstBlend。它们是混合模式的枚举,但我们可以使用的最佳类型是Float,默认情况下为_SrcBlend设置为1,_DstBlend设置为0。


Properties {
		_BaseColor("Color", Color) = (1.0, 1.0, 1.0, 1.0)
		_SrcBlend ("Src Blend", Float) = 1
		_DstBlend ("Dst Blend", Float) = 0
	}


为了便于编辑,我们可以添加Enum关键字到属性中,并使用完全限定的UnityEngine.Rendering.BlendMode枚举类型作为参数。


[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend", Float) = 1
		[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend", Float) = 0


unity 怎么编辑html控制颜色_Blend_03


现在所显示的结果就是我们设置的默认值,源颜色的混合模式是1,目标颜色的混合模式是0,就是说源颜色会将目标颜色完全覆盖。

标准的透明模式源颜色的混合模式是设为SrcAlpha,目标颜色的混合模式是OneMinusSrcAlpha,颜色的计算公式是



混合模式的定义是在Pass块中实现,语法是Blend关键字后面跟着两个混合模式。


Pass {
			Blend [_SrcBlend] [_DstBlend]

			HLSLPROGRAM
			…
			ENDHLSL
		}


透明渲染通常不会写入深度缓冲区,因为这没有多大意义,甚至可能产生不想要的结果。我们可以通过ZWrite语句控制写入深度信息。跟混合模式一样,我们再定义一个_ZWrite字段来控制深度写入功能。


Blend [_SrcBlend] [_DstBlend]
			ZWrite [_ZWrite]


为了便于编辑,我们来设计一个on-off开关来控制写入深度信息的设置。


[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend", Float) = 1
		[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend", Float) = 0
		[Enum(Off, 0, On, 1)] _ZWrite ("Z Write", Float) = 1


unity 怎么编辑html控制颜色_unity片元着色器中获取屏幕坐标_04


纹理贴图

在第三章中,我们曾经使用了一张灰度图来创建非均匀半透明材质,现在来让我们的unlit shader也支持这个功能吧。

首先我们在Properties块中添加一个_BaseMap纹理属性,贴图的类型是2D纹理,同时我们给它设置默认值,采用的是unity自带的白色纹理。


_BaseMap("Texture", 2D) = "white" {}
		_BaseColor("Color", Color) = (1.0, 1.0, 1.0, 1.0)


unity 怎么编辑html控制颜色_unity物体设置透明度_05


接下来我们需要把纹理上传到GPU内存,这由unity来为我们完成,不过shader需要有相应纹理的句柄,这个我们可以像定义字段那样去定义它,这里我们使用TEXTURE2D这个宏来定义,把字段名像参数一样传递给它即可。另外,我们还需要给纹理贴图定义一个采样状态来控制它的采样模式,这取决于它的包装和过滤模式。这通过SAMPLE宏来完成,就像TEXTURE2D一样,不过需要在名字前面加个前缀sample。

纹理跟采样器状态是shader的资源,并不可以由实例提供,所以我们需要将它们定义为全局变量,把它们定义在UnlitPass.hlsl文件中。


TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
	UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)


此外,unity还提供了一个float4变量来控制纹理的平铺和偏移,这个变量跟纹理属性同名,不过需要加_ST后缀。这个变量可以放到UnityPerMaterial缓冲区中,这样可以按实例设置。


UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
	UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
	UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)


要对纹理进行采样我们需要知道纹理坐标,这也是顶点自带的一个属性,unity的模型顶点最多可以设置四组纹理坐标,这里我们只需要第一组纹理坐标,让我们在Attributes结构体中添加一个新的字段baseUV,后面需要加一个语义:TEXCOORD0,表示读的是第一组纹理坐标。


struct Attributes {
	float3 positionOS : POSITION;
	float2 baseUV : TEXCOORD0;
	UNITY_VERTEX_INPUT_INSTANCE_ID
};


接着我们需要把纹理坐标传递给片元处理函数,因为最终是由它来完成纹理采样的。在Varyings结构体中,我们新添加一个字段baseUV,后面给它添加语义VAR_BASE_UV。


struct Varyings {
	float4 positionCS : SV_POSITION;
	float2 baseUV : VAR_BASE_UV;
	UNITY_VERTEX_INPUT_INSTANCE_ID
};


当我们在UnlitPassVertex中对顶点坐标进行变换运算的时候,我们也可以对纹理坐标进行变换运算,可以对它进行缩放和平移,这些数据都存储在_BaseMap_ST中,XY保存的是x,y方向的缩放比例,ZW保存的是x,y方向的偏移量。


Varyings UnlitPassVertex (Attributes input) {
	…

	float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
	output.baseUV = input.baseUV * baseST.xy + baseST.zw;
	return output;
}


最后我们在UnlitPassFragment函数中对纹理进行采样,我们采用的是SAMPLE_TEXTURE2D宏,然后将纹理,采样器以及纹理坐标作为参数传给SAMPLE_TEXTURE2D。最后返回的结果是纹理采样的值与_BaseColor的乘积。


float4 UnlitPassFragment (Varyings input) : SV_TARGET {
	UNITY_SETUP_INSTANCE_ID(input);
	float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV);
	float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
	return baseMap * baseColor;
}


unity 怎么编辑html控制颜色_unity片元着色器中获取屏幕坐标_06


alpha裁剪

透视表面还有一种方法是在模型表面切孔,shader可以通过丢弃某些片元来做到这一点。但是这样做会产生硬边,而不像我们现在看到的平滑过渡,这种技术叫做alpha裁剪。它的原理是设置一个裁剪阈值。alpha值低于此阈值的片段将被丢弃,而所有其它片段将保留。

我们新加一个shader属性_Cutoff,数据类型是Range(0.0,1.0),因为alpha值范围就在0和1之间,默认值我们设为0.5.


_BaseColor("Color", Color) = (1.0, 1.0, 1.0, 1.0)
		_Cutoff ("Alpha Cutoff", Range(0.0, 1.0)) = 0.5


我们同样把这个值添加到UnityPerMaterial结构体中,这样可以按实例设置。


UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
	UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)


丢弃片元使用的是clip函数,当我们传递的参数小于等于0时就会丢弃该片元。


float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV);
	float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
	float4 base = baseMap * baseColor;
	clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
	return base;


unity 怎么编辑html控制颜色_unity片元着色器中获取屏幕坐标_07


unity 怎么编辑html控制颜色_Blend_08


一个材质经常会用到透明度混合或者alpha裁剪,当时一般不会同时使用。一个典型的裁剪材料除了被丢弃的片元外是完全不透明的,并且会写入深度缓存区。它使用AlphaTest队列,这意味着它是在所有完全不透明物体被渲染完之后才会被渲染。这样做是因为丢弃片元使某些GPU优化无法实现,因为不再假定三角形完全覆盖了它们后面的内容。而首先绘制完全不透明物体,那么alpha裁剪的对象被覆盖的部分则无需再处理。


unity 怎么编辑html控制颜色_Blend_09


unity 怎么编辑html控制颜色_混合模式_10


但是裁剪操作我们必须要保证是在需要时才执行,为此我们需要添加一个切换开关,使用关键字Toggle创建一个属性_CLIPPING,shader属性的名字是_Clipping,在这里没有太大意义。


_Cutoff ("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
		[Toggle(_CLIPPING)] _Clipping ("Alpha Clipping", Float) = 0


unity 怎么编辑html控制颜色_Blend_11


开启这个开关的时候,_CLIPPING关键字将会被添加到材质的活性关键字列表中,关闭则会删除。然而这个关键字单独是没有任何作用的,我们需要告诉unity根据是否定义了这个关键字来编译着色器的不同版本。为此,我们将#pragma shader_feature _CLIPPING添加到其Pass块的指令中。


#pragma shader_feature _CLIPPING
			#pragma multi_compile_instancing


现在,unity就会将我们的shader编译成两个版本,一个定义了_CLIPPING一个没有定义。它将会产生一个或两个变体,这取决于我们怎么定义我们的材质。


#if defined(_CLIPPING)
		clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
	#endif


因为cutoff是UnityPerMaterial缓存的一部分,它可以按实例设置。因此,让我们将该功能添加到PerObjectMaterialProperties中。 除了需要在属性块上调用SetFloat而不是SetColor之外,它的作用与颜色相同。


static int baseColorId = Shader.PropertyToID("_BaseColor");
	static int cutoffId = Shader.PropertyToID("_Cutoff");

	static MaterialPropertyBlock block;

	[SerializeField]
	Color baseColor = Color.white;

	[SerializeField, Range(0f, 1f)]
	float cutoff = 0.5f;

	…

	void OnValidate () {
		…
		block.SetColor(baseColorId, baseColor);
		block.SetFloat(cutoffId, cutoff);
		GetComponent<Renderer>().SetPropertyBlock(block);
	}


unity 怎么编辑html控制颜色_unity片元着色器中获取屏幕坐标_12


unity 怎么编辑html控制颜色_unity 怎么编辑html控制颜色_13