unity透明通道加颜色_半透明物体的描边和投影

10b26565e4e91a23933948c66d220a92.png

起因是有人谈到HDRP更新了半透明阴影,然后想了想透明物体在NPR中还是比较常见的,至少我上次就看到有人发了个模型,就因为衣服上的丝绸部分没有加描边,导致看上去完成度很低。

透明物体有丝绸,雨衣(透明胶制服装),玻璃杯等等,还算挺常见的,而且都有生成描边和投影的需求。

正好把一些遗留的问题解决了。


半透描边大概就三种方案,1.按透明绘制但是写入深度,2.用模板缓存代替深度,3.当不透明物体渲染并用GrabPass来模拟透明。我这里用的是最简单的第一种。

和头发类似,先绘制背面,再绘制正面,然后再扩边绘制背面的描边,3个PASS,需要保证模型的内面的深度更高。封闭物体都具有这个性质。

Tags{ "RenderType" = "Transparent"  "Queue" = "Transparent" }
Pass
{
	Blend SrcAlpha OneMinusSrcAlpha
	ColorMask RGB
	Cull Front

	CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag

	ENDCG
}

Pass
{
	Blend SrcAlpha OneMinusSrcAlpha
	ColorMask RGB
	Cull Back

	CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag

	ENDCG
}

Pass
{
	Blend SrcAlpha OneMinusSrcAlpha
	ColorMask RGB
	Cull Front

	CGPROGRAM
	#pragma vertex vert_line
	#pragma fragment frag_line

	ENDCG
}

用普通的透明shader和backface扩边替换vert,frag,vert_line,frag_line就可以得到这个效果,因为具体实现各人的都不同,就不写出来了。

会出现的瑕疵有:

1.两个透明物体并交叉包含的时候会互相遮挡,而且当透明排序变化的时候会跳变

559fa26d24c5afe94941c3c4227ed5ee.png

2894e5d7cac021e2df91f67e6f175091.png

透明物体的固有问题,没什么好办法,别让这种情况出现就好。

2.粒子处于模型的前方,而且发射出的粒子进入球内的时候会被遮挡。而如果粒子处于模型后方,无论如何都不会被遮挡。

d55bd7a4a82873965fafaec17a615a1e.png

这其实也是半透明自己的问题。如果在意半透和非半透交叉产生的交线,可以开启软粒子回避(其实球和Cude的交线瑕疵也能这样回避)


半透阴影其实可行的就网点这种,其他的诸如给shadowmap加透明通道,都需要额外增加成本,而且难以处理多层叠加的问题(两层半透之间的物体的阴影接收也是需要处理的),真正完美的方案需要把shadowmap存UAV里实现多层shadowmap(还要带透明度),这可算了吧。

网点半透需要修改ShadowCast,做法是给原Shader增加一个LightMode标记为ShadowCaster的Pass,并重新定义绘制逻辑。

Pass{
	Tags {"LightMode" = "ShadowCaster"}
	Cull Off
	CGPROGRAM
	#pragma vertex vert_shadow
	#pragma fragment frag_shadow
	#pragma multi_compile_shadowcaster
	ENDCG
}

绘制阴影的时候需要根据原图的透明度,输出下图这样的网点图案来模拟透明效果(为了方便观察故意把网点放大了)

7c055d729977e3ab0ff36ced3c11bcd2.png

fc59680edcd592bccc994fb8a56fe466.png

ae05a11664e29f45ef2916f54a66f690.png

网点的生成没有使用网点纹理,而使用了一个数值计算的结果:

void transparencyClip(float alpha, float2 screenPos)
{
	// Screen-door transparency: Discard pixel if below threshold.
	float4x4 thresholdMatrix =
	{ 1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
	  13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
	   4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
	  16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0
	};
	float4x4 _RowAccess = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
	clip(alpha - thresholdMatrix[fmod(screenPos.x, 4)] * _RowAccess[fmod(screenPos.y, 4)]);
}

数值计算一般比用纹理采样要快。

原模型加入纹理后如下图

9f5bc9da965d0cb47c6e325117254f2d.png

采样的瑕疵很难避免,所以需要开启软阴影。

c43d7fb42219a49b9985490f5d84f5d2.png

使用较低的阴影质量时的情况:

312a66e08bb50774445299735a6b5d9a.png

瑕疵基本可以接受,但在镜头移动的时候会产生一定的抖动现象,因此最好还是保持较高的精度。

而描边本身的阴影……因为ShadowCast只能存在一个。如果想生成这个可能只能复制一个单独的描边材质了,这个就算了吧。

需要注意的是,绘制阴影的时候需要严格对齐像素,所以需要获得shadowmap纹理的具体大小,而_ScreenParams那一系列函数是无效的。即使定下某个固定缩放数值,如果使用了层级阴影,切换到不同级别的时候也无法统一。

这里获取具体的屏幕坐标用了VPOS,具体写法可查看代码:

//shadowCast
struct v2f_shadow
{
	float2 uv : TEXCOORD2;
};

void transparencyClip(float alpha, float2 screenPos)
{
	// Screen-door transparency: Discard pixel if below threshold.
	float4x4 thresholdMatrix =
	{ 1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
		13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
		4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
		16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0
	};
	float4x4 _RowAccess = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
	clip(alpha - thresholdMatrix[fmod(screenPos.x, 4)] * _RowAccess[fmod(screenPos.y, 4)]);
}
v2f_shadow vert_shadow(appdata_base v,out float4 outpos : SV_POSITION)
{
	v2f_shadow o;
	TRANSFER_SHADOW_CASTER_NOPOS(o, outpos)
	o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
	return o;
}
float4 frag_shadow(v2f_shadow i, UNITY_VPOS_TYPE screenPos : VPOS) :SV_Target
{
	fixed4 col = tex2D(_MainTex, i.uv) * _Color;
	transparencyClip(col.a,screenPos.xy);
	SHADOW_CASTER_FRAGMENT(i)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>