Unity Shader 学习笔记(19) 屏幕后处理效果、调整亮度、饱和度、对比度
Unity Shaderlab: Object Outlines
Unity Shader-描边效果
完整源码:https://github.com/ZeroChiLi/ShaderTest
添加额外相机(位置等信息等同原相机),将指定Layer的可见物体渲染出物体所占的面积,并把渲染的纹理存到一个临时渲染纹理(RenderTexture)中,再通过这个纹理扩展出外边,最后将这个外边和原场景纹理混合即可。
DrawOutline类继承于PostEffectsBase类,介绍见这里。
原始场景:包含左边一个有剔除片元的立方体,右边半透明立方体,以及没有在”Outline”层的右边小球。
额外相机不需要enable,渲染处理都在代码中实现
额外相机默认渲染效果
DrawOutline类,添加在主相机对象。在对象Awake时初始化额外相机(不需要active 或 enable)。
private void Awake()
{
additionalCamera.CopyFrom(MainCamera);
additionalCamera.clearFlags = CameraClearFlags.Color;
additionalCamera.backgroundColor = Color.black;
additionalCamera.cullingMask = 1 << LayerMask.NameToLayer("Outline"); // 只渲染"Outline"层的物体
}
小技巧:直接Fallback OFF。
DrawOutline类中,OnRenderImage接口实现屏幕后处理。
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (TargetMaterial != null && drawOccupied != null && additionalCamera != null)
{
RenderTexture TempRT = RenderTexture.GetTemporary(source.width, source.height, 0);
TempRT.Create();
additionalCamera.targetTexture = TempRT;
// 额外相机中使用shader,绘制出物体所占面积
additionalCamera.RenderWithShader(drawOccupied, "");
Graphics.Blit(TempRT, destination);
TempRT.Release();
}
else
Graphics.Blit(source, destination);
}
绘制所在面积的shader:DrawOccupied.shader。
Shader "Custom/DrawOccupied"
{
FallBack OFF
}
基于上面面积,扩展每个像素的颜色(类似高斯模糊),黑色值全为0,所以无法扩展
最后将黑色部分,和原占面积(粉红色部分),渲染为原场景。
在上一步绘制完面积后,添加修改如下代码:
// TargetMaterial为含有描绘混合shader的材质
// 设置相关属性,source为接口传入的原场景纹理
TargetMaterial.SetTexture("_SceneTex", source);
TargetMaterial.SetColor("_Color", outlineColor);
TargetMaterial.SetInt("_Width", outlineWidth);
TargetMaterial.SetInt("_Iterations", iterations);
// 使用描边混合材质实现描边效果
Graphics.Blit(TempRT, destination, TargetMaterial, 0);
DrawOutline.shader:绘制描边效果的shader
Shader "Custom/DrawOutline"
{
Properties
{
_MainTex("Main Texture",2D)="black"{} // 绘制完物体面积后纹理
_SceneTex("Scene Texture",2D)="black"{} // 原场景纹理
_Color("Outline Color",Color) = (0,1,0,1) // 描边颜色
_Width("Outline Width",int) = 4 // 描边宽度(像素级别)
_Iterations("Iterations",int) = 3 // 描边迭代次数(越高越平滑,消耗越高,复杂度O(n^2))
}
SubShader
{
Pass
{
CGPROGRAM
sampler2D _MainTex;
float2 _MainTex_TexelSize;
sampler2D _SceneTex;
fixed4 _Color;
float _Width;
int _Iterations;
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
return o;
}
half4 frag(v2f i) : COLOR
{
// 迭代为奇数,保证对称
int iterations = _Iterations * 2 + 1;
float ColorIntensityInRadius;
float TX_x = _MainTex_TexelSize.x * _Width;
float TX_y = _MainTex_TexelSize.y * _Width;
// 类似高斯模糊,只是这里的核权重不重要,只要最后计算大于0,说明该像素属于外边范围内。
for(int k = 0;k < iterations;k += 1)
for(int j = 0;j < iterations;j += 1)
ColorIntensityInRadius += tex2D(_MainTex, i.uv.xy + float2((k - iterations/2) * TX_x,(j - iterations/2) * TX_y));
// 如果该像素有颜色(原来所占面积),或者该像素不在外边范围内,直接渲染原场景。否则就渲染为外边颜色。
if(tex2D(_MainTex,i.uv.xy).r > 0 || ColorIntensityInRadius == 0)
return tex2D(_SceneTex, i.uv);
else
return _Color.a * _Color + (1 - _Color.a) * tex2D(_SceneTex, i.uv);// 通过输入颜色的透明度来混合原场景。
}
ENDCG
}
}
}