-
课时70 Fragment shader - Alpha 与 blend 运算
1.Zwrite:深度写入,默认是Zwrite On。如果需要绘制实心物体需要开启,如果绘制半透明物体,需要关闭深度写入。见:ShaderLab: Culling & Depth Testing
2.Ztest:默认LEqual,当深度值小于等于深度缓冲区的值,则写入(用Zwrite)并覆盖深度缓冲区对应位置的值。但是,如果Zwrite 为off,则只会输出颜色,不会写入深度缓冲区。
3.半透明红色物体如果不关闭深度写入,某些情况下会不显示身后的其他半透明物体,原理是红色物体深度值较小,破坏了蓝色半透明物体的Alpha颜色混合计算结果。关闭深度写入之后,每次Alpha Test只比较已存在的深度值,如果小于等于则只输出颜色混合结果。
4.代码:
Shader "Custom/Lesson70_1"
{
//课时70 Fragment shader - Alpha 与 blend 运算
SubShader
{
Tags{"queue"="transparent"}
Pass
{
blend srcalpha oneminussrcalpha
Zwrite Off//半透明物体需关闭深度写入
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 pos:POSITION;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 color = fixed4(1,0,0,0.5);
return color;
}
ENDCG
}
}
}
效果:
-
课时71 Fragment shader 7 - 实现半透明着色 1
1.需求:要求胶囊体被墙体遮挡部分显示半透明。
2.主要用到Alpha混合,Zwrite默认。
3.如果想让下半部分显示,需要改写深度对比方式,以让深度大于墙体的被遮挡部分写入深度缓存。但是会导致上半部分消失。
4.如果显示上半部分,需要在第二个通道里面,深度对比方式改为小于等于,Alpha改为1。此时产生新的问题,第二个通道的颜色会覆盖。
5.分析原因:第一个通道的下半部分由于被写入缓冲区中,第二个通道渲染时下半部分会与上个通道渲染结果进行深度对比,因为深度值一样,且对比方式为LEqual,所以会覆盖下半部分已有的蓝色的深度值。解决方式一:关闭第一个通道的深度写入功能,只输出颜色信息。
6.解决方式二:第一个通道的深度写入功能可关可不关,第二个通道的深度对比方式改为小于。
7.代码:
Shader "Custom/Lesson71"
{
//课时71 Fragment shader 7 - 实现半透明着色 1
SubShader
{
Tags{"queue"="transparent"}
Pass
{
blend srcalpha oneminussrcalpha
ZTest Greater//关键点,覆盖前景——墙
// Zwrite Off//可关可不关,半透明物体需关闭深度写入
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 pos:POSITION;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 color = fixed4(0,0,1,0.5);
return color;
}
ENDCG
}
//=====================负责渲染上半部分================================
Pass
{
// blend srcalpha oneminussrcalpha //可混合可不混合。上下两个通道分别渲染两个部分,没有重合部分和混合的必要
ZTest Less //关键点,覆盖背景,但不覆盖上一通道
// Zwrite Off//可关可不关深度写入
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 pos:POSITION;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 color = fixed4(1,0,0,1);
return color;
}
ENDCG
}
}
}
8.遗留问题:透明墙体挡在前面时,下半部分不再显示蓝色。
-
课时72 Fragment shader 7 - 实现半透明着色 2
1.需求:让下面的胶囊体不论是被不透明墙还是透明墙挡在前面,上面部分显示蓝色,下半部分显示黄色。
2.分析原因:由于红色半透明墙体的深度关闭,导致胶囊体下半部分无法和它的深度做对比(对比方式为Greater),从而无法输出颜色信息。第二个通道的渲染顺序为背景-第二通道-墙体,它只简单输出颜色。
3.解决方式一:开启半透明墙体的深度写入,由于半透明物体的深度写入一般都要求关闭,所以不建议。
4.解决方式二:使用摄像机深度贴图(Camera’s Depth Texture),但是摄像机深度贴图只记录不透明物体渲染队列,所以不可行。
5.解决方式三:使用摄像机置换Shader(Rendering with Replaced Shaders)。
6.使用方式:①编写一个置换透明物体用的shader,"RenderType"="Transparent",用于基于RenderType来置换并记录透明物体深度信息(注:半透明墙体shader需加"RenderType"="Transparent")。
Shader "Custom/Lesson72_ReplacementShader"
{
//课时72 Fragment shader 7 - 实现半透明着色 2
SubShader
{
Tags{"RenderType"="Transparent"}//只会置换具有相同RenderType的shader
Pass
{
// blend srcalpha oneminussrcalpha
// Zwrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 pos:POSITION;
float2 depth:TEXCOORD0;//存储深度信息
};
v2f vert (appdata_base v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.depth=o.pos.zw;
return o;
}
fixed4 frag (v2f IN) : COLOR
{
float r=IN.depth.x/IN.depth.y;//得到深度信息
r=Linear01Depth(r);//换算成0~1
fixed4 color = fixed4(r,0,0,0.5);//只用r信息,r存储深度信息
return color;
}
ENDCG
}
}
}
②在Start中调用camera.SetReplacementShader()(结束对应camera.ResetReplacementShader())。
using UnityEngine;
public class ReplacementTransparentShader : MonoBehaviour {
void Start () {
Camera.main.SetReplacementShader(Shader.Find("Custom/Lesson72_ReplacementShader"),"RenderType");
}
}
7.运行前后效果:
8.最终运行时效果: