|
Unity中除了基础的shader制作和光照烘焙的工作模块外还有一个稍微更高级那么一点儿的模块。后处理(PostProcessing)在现在市面上流行的手机游戏和电脑游戏中是非常常见的高级功能和效果。但由于其对机能要求比较高因此。大部分只会在高端手机和中高端电脑上使用。且在移动平台基本都是经过阉割后的版本。完整的后处理一般是需要开启HDR功能才能达到最佳的效果。不过现阶段能支持HDR的手机比较少,因此手机平台的(PostProcessing)基本都是利用一些奇技淫巧来模拟HDR的效果。不过这也是前人留下的智慧,刚好笔者最近也搜集了不少文章并自己写了一套后处理模块。就在此分享出来供大家学习使用,欢迎各位大佬的指正和建议。
Unity的Bloom实现:
先看一下没有后处理的效果:
最精简的显示。没有Bloom的支持的效果
Bloom可以帮助设计师抓取画面中最亮的地方形成颜色的发散。
Bloom的写法网上有非常多的模板。随便找一个拿来来改改就可以得到我们自己的效果。Bloom最难处理的地方是如何在手机平台也能运行且不会占用太多的渲染资源。
最常规的Bloom实现:
PostEffectBase基类(直接拿unity入门精要里的):
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectsBase : MonoBehaviour
{
// Called when start
protected void CheckResources()
{
bool isSupported = CheckSupport();
if (isSupported == false)
{
NotSupported();
}
}
// Called in CheckResources to check support on this platform
protected bool CheckSupport()
{
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
{
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}
return true;
}
// Called when the platform doesn't support this effect
protected void NotSupported()
{
enabled = false;
}
protected void Start()
{
CheckResources();
}
// Called when need to create the material used by this effect
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
{
if (shader == null)
{
return null;
}
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported)
{
return null;
}
else
{
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
}
尝试用网上的bloom公式书写一个bloom的效果。
可以很明显的看到第一个Pass抓取的是RT中的最亮的地方。
Bloom的shader中需要进行书写如何提取出需要进行bloom处理的地方。
亮度提取的核心代码。可以根据自己的需要进行修改。
之后还要进行Blur和最后Bloom的pass书写。
Bloom的shader(Blur 和Bloom的Pass直接使用网上的就行。大家基本都差不多。大同小异):
// ---------------------------【泛光 Bloom】---------------------------
Shader "PostProcessEffect/Bloom"
{
// ---------------------------【属性】---------------------------
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
// ---------------------------【子着色器】---------------------------
SubShader
{
//后处理效果一般都是这几个状态
ZTest Always
Cull Off
ZWrite Off
Fog{ Mode Off }
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _BlurTex;
float4 _offsets;
float _LuminanceThreshold;
fixed4 _BloomColor;
float _BloomFactor;
// ---------------------------【亮度提取 - start】---------------------------
struct v2fExtBright
{
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
};
// 顶点着色器
v2fExtBright vertExtractBright(appdata_img v)
{
v2fExtBright o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0.0)
o.uv = o.uv * float2(1.0, -1.0) + float2(0.0, 1.0);
#endif
return o;
}
//亮度提取
fixed luminance(fixed4 color) {
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
//================获取a通道作为bloom亮度提取区间=========================
//half luminance(fixed4 color)
// {
// half l = saturate(1 - color.a);
// // return l;
// l = 1 / ((1 / l) - 1);
// return l;
// // return (0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b) * l;
//}
// 片元着色器
fixed4 fragExtractBright(v2fExtBright i) : SV_Target {
fixed4 color = tex2D(_MainTex, i.uv);
// clamp 约束到 0 - 1 区间
fixed val = clamp(luminance(color) - _LuminanceThreshold, 0.0, 1.0);
//================获取a通道作为bloom亮度提取区间=========================
//half val = luminance(color);
return color * val;
}
// ---------------------------【亮度提取 - end】---------------------------
// ---------------------------【高斯模糊 - start】---------------------------
struct v2fBlur
{
float4 pos : SV_POSITION; //顶点位置
float2 uv : TEXCOORD0; //纹理坐标
float4 uv01 : TEXCOORD1; //一个vector4存储两个纹理坐标
float4 uv23 : TEXCOORD2; //一个vector4存储两个纹理坐标
};
//高斯模糊顶点着色器
v2fBlur vertBlur(appdata_img v)
{
v2fBlur o;
o.pos = UnityObjectToClipPos(v.vertex);
//uv坐标
o.uv = v.texcoord.xy;
//计算一个偏移值,offset可能是(1,0,0,0)也可能是(0,1,0,0)这样就表示了横向或者竖向取像素周围的点
_offsets *= _MainTex_TexelSize.xyxy;
//由于uv可以存储4个值,所以一个uv保存两个vector坐标,_offsets.xyxy * float4(1,1,-1,-1)可能表示(0,1,0-1),表示像素上下两个
//坐标,也可能是(1,0,-1,0),表示像素左右两个像素点的坐标,下面*2.0,*3.0同理
o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);
o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;
return o;
}
//高斯模糊片段着色器
fixed4 fragBlur(v2fBlur i) : SV_Target
{
fixed4 color = fixed4(0,0,0,0);
color += 0.4026 * tex2D(_MainTex, i.uv);
color += 0.2442 * tex2D(_MainTex, i.uv01.xy);
color += 0.2442 * tex2D(_MainTex, i.uv01.zw);
color += 0.0545 * tex2D(_MainTex, i.uv23.xy);
color += 0.0545 * tex2D(_MainTex, i.uv23.zw);
return color;
}
// ---------------------------【高斯模糊 - end】---------------------------
// ---------------------------【Bloom(高斯模糊和原图叠加) - start】---------------------------
struct v2fBloom {
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
};
// 顶点着色器
v2fBloom vertBloom(appdata_img v) {
v2fBloom o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
// 片元着色器
fixed4 fragBloom(v2fBloom i) : SV_Target {
//对原图进行uv采样
fixed4 mainColor = tex2D(_MainTex, i.uv);
//对模糊处理后的图进行uv采样
fixed4 blurColor = tex2D(_BlurTex, i.uv);
//输出 = 原始图像 + 模糊图像 * bloom颜色 * bloom权值
fixed4 resColor = mainColor + blurColor * _BloomColor * _BloomFactor;
return resColor;
}
// ---------------------------【Bloom - end】---------------------------
ENDCG
// 亮度提取
Pass {
Name &#34;#Bright&#34;
CGPROGRAM
#pragma vertex vertExtractBright
#pragma fragment fragExtractBright
ENDCG
}
//高斯模糊
Pass {
Name &#34;#vertBlur&#34;
CGPROGRAM
#pragma vertex vertBlur
#pragma fragment fragBlur
ENDCG
}
// Bloom
Pass {
Name &#34;#vertBloom&#34;
CGPROGRAM
#pragma vertex vertBloom
#pragma fragment fragBloom
ENDCG
}
}
}
继续书写一个Bloom.cs。这里就直接贴文件了。其实都是直接网上的代码进行修改和合成就行。
Bloom.cs
8.3K
· 百度网盘
接下来进行一些我们自己需要的修改:(基类可以根据我们自己的需要进行自行修改。)
PostPrcessing基类:
using UnityEngine;
using System.Collections;
using UnityEngine.Rendering;
//[ExecuteInEditMode]
//[RequireComponent(typeof(Camera))]
public abstract class PostEffectsBase : MonoBehaviour
{
public bool enable = true;
[HideInInspector] public bool needDepth = false;
protected bool support;
protected PostProcessProfile profile;
protected Camera cam;
public readonly string displayName;
protected virtual void OnEnable()
{
profile = FindObjectOfType<PostProcessProfile>();
if (profile == null)
return;
cam = profile.cam;
if (profile.effects == null) profile.effects = new System.Collections.Generic.List<PostEffectsBase>();
profile.effects.Add(this);
}
protected virtual void Clear()
{
if (profile != null && profile.effects != null) profile.RemoveEffect(this);
cam = null;
}
protected virtual void OnDisable()
{
Clear();
}
protected virtual void OnDestroy()
{
Clear();
}
// Called when start
protected void CheckResources()
{
bool isSupported = CheckSupport();
if (isSupported == false)
{
NotSupported();
}
}
// Called in CheckResources to check support on this platform
protected bool CheckSupport()
{
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
{
Debug.LogWarning(&#34;This platform does not support image effects or render textures.&#34;);
return false;
}
return true;
}
// Called when the platform doesn&#39;t support this effect
protected void NotSupported()
{
enabled = false;
}
protected void Start()
{
CheckResources();
}
// Called when need to create the material used by this effect
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
{
if (shader == null)
{
return null;
}
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported)
{
return null;
}
else
{
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
//声明抽象类名。
public abstract void Render(RenderTexture src, RenderTexture dst);
}
书写一个作为预览效果用的Profiles:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteInEditMode, ImageEffectAllowedInSceneView]
public class PostProcessProfile : MonoBehaviour
{
public List<PostEffectsBase> effects;
public Camera cam;
public bool enbaleFXAA;
#if UNITY_EDITOR
private RenderTexture dst;
#endif
//private Shader fxaaShader;
public Shader fxaaShader;
private Material fxaaMaterial = null;
public Material fxaaMaterial_Get
{
get
{
fxaaMaterial = CheckShaderAndCreateMaterial(fxaaShader, fxaaMaterial);
return fxaaMaterial;
}
}
private static Shader blitShader;
private static Material blitMaterial = null;
public static Material BlitMaterial
{
get
{
if (blitMaterial != null) return blitMaterial;
if (blitShader == null) blitShader = Shader.Find(&#34;Unlit/PostProcessBlit&#34;);
if (blitShader == null || !blitShader.isSupported) return null;
blitMaterial = new Material(blitShader);
blitMaterial.hideFlags = HideFlags.DontSave;
return blitMaterial;
}
}
// Called when need to create the material used by this effect
private Material CheckShaderAndCreateMaterial(Shader shader, Material material)
{
if (shader == null)
{
return null;
}
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported)
{
return null;
}
else
{
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
private void Awake()
{
cam = GetComponent<Camera>();
}
private void Start()
{
}
public void RemoveEffect(PostEffectsBase effect)
{
if (effect.needDepth) cam.depthTextureMode = DepthTextureMode.None;
effects.Remove(effect);
}
private void OnPreRender()
{
cam.depthTextureMode = DepthTextureMode.DepthNormals;
if (effects != null)
{
for (int i = 0; i < effects.Count; ++i)
{
if (effects == null || !effects.enable) continue;
if (effects.needDepth)
{
cam.depthTextureMode = DepthTextureMode.DepthNormals;
break;
}
}
}
}
#if UNITY_EDITOR
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (effects == null || effects.Count == 0)
{
if (enbaleFXAA && fxaaMaterial_Get != null) Graphics.Blit(source, destination, fxaaMaterial);
else Graphics.Blit(source, destination);
return;
}
if (dst == null)
{
dst = RenderTexture.GetTemporary(source.width, source.height, 0, source.format);
}
else if (dst.width != source.width || dst.height != source.height || dst.format != source.format)
{
RenderTexture.ReleaseTemporary(dst);
dst = RenderTexture.GetTemporary(source.width, source.height, 0, source.format);
}
var src = source;
var dt = dst;
for (int i = 0; i < effects.Count; ++i)
{
var effect = effects;
if (effect == null || !effect.enable) continue;
effect.Render(src, dt);
// 交换指针
var temp = src;
src = dt;
dt = temp;
}
if (enbaleFXAA && fxaaMaterial_Get != null)
{
fxaaMaterial.SetVector(&#34;_Width_Height_Inverse&#34;, new Vector4(1.0f / src.width, 1.0f / src.height));
Graphics.Blit(src, destination, fxaaMaterial);
var temp = src;
src = dst;
dst = temp;
}
else Graphics.Blit(src, destination);
}
#endif
private void OnDestroy()
{
#if UNITY_EDITOR
if (dst != null)
{
RenderTexture.ReleaseTemporary(dst);
}
#endif
effects.Clear();
effects = null;
cam = null;
}
}OK。Bloom的效果差不多就做完了。不过其实效果和unity原版还是有些区别。因为我们的采样公式和官方不同。不过不要紧,只要知道了书写方法和实现方式。剩下就是调整和测试了。
自定义一个颜色校正的效果:
颜色校正属于比较初级的后处理效果。基本用在调整整体画面的饱和度和色相上。shader和CS脚本的处理和Bloom处理步骤类似。比较简单。
颜色校正的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteInEditMode]
public class ColorModulation : PostEffectsBase
{
public Shader shader;
private Material mat = null;
public Material material
{
get
{
//shader = Shader.Find(&#34;PostProcessing/ColorModulation&#34;);
mat = CheckShaderAndCreateMaterial(shader, mat);
return mat;
}
}
[Range(0, 8)]
public int downSample = 1;
public Color Color;
[Range(0, 5)]
public float Contrast = 1;
[Range(-5, 5)]
public float Brightness = 0.5f;
[Range(-5, 5)]
public float Saturation = 1;
[Range(-5, 5)]
public float Exposure;
[Range(-5, 5)]
public float Gamma;
[Range(0.0f, 1.0f)]
public float temperature = 0.5f;
[Range(0.0f, 1.0f)]
public float tint = 0.5f;
public override void Render(RenderTexture src, RenderTexture dst)
{
if (material == null)
{
Graphics.Blit(src, dst);
return;
}
int width = src.width >> downSample;
int height = src.height >> downSample;
var rt0 = RenderTexture.GetTemporary(width, height, 0, src.format);
Graphics.Blit(src, rt0);
//mat.SetVector(&#34;_HSVMul&#34;, new Vector4(Hue, saturation, value));
mat.SetColor(&#34;_Color&#34;, Color);
mat.SetFloat(&#34;_Contrast&#34;, Contrast);
mat.SetFloat(&#34;_Brightness&#34;, Brightness);
mat.SetFloat(&#34;_Saturation&#34;, Saturation);
mat.SetFloat(&#34;_Exposure&#34;, Exposure);
mat.SetFloat(&#34;_Gamma&#34;, Gamma);
mat.SetVector(&#34;_Temperature_Tint&#34;, new Vector4(temperature, tint));
Graphics.Blit(rt0, dst, mat, 0);
RenderTexture.ReleaseTemporary(rt0);
}
}
颜色校正的shader:
Shader &#34;Unlit/ColorModulation&#34;
{
Properties
{
_MainTex (&#34;Texture&#34;, 2D) = &#34;white&#34; {}
}
SubShader
{
Cull Off
ZWrite Off
ZTest Always
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include &#34;UnityCG.cginc&#34;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed _Contrast;
fixed _Brightness;
fixed _Saturation;
fixed _Exposure;
fixed _Gamma;
half2 _Temperature_Tint;
half3 WhiteBalance(half3 col, half temperature, half tint)
{
half t1 = temperature * 1.66667;
half t2 = tint * 1.66667;
half x = 0.31271 - t1 * (t1 < 0.0 ? 0.1 : 0.05);
half standardIlluminantY = 2.87 * x - 3.0 * x * x - 0.27509507;
half y = standardIlluminantY + t2 * 0.05;
half3 w1 = half3(0.949237, 1.03542, 1.08728);
half Y = 1.0;
half X = Y * x / y;
half Z = Y * (1.0 - x - y) / y;
half L = 0.7328 * X + 0.4296 * Y - 0.1624 * Z;
half M = -0.7036 * X + 1.6975 * Y + 0.0061 * Z;
half S = 0.0030 * X + 0.0136 * Y + 0.9834 * Z;
half3 w2 = half3(L, M, S);
half3 balance = w1 / w2;
float3x3 LIN_2_LMS_MAT =
{
3.90405e-1, 5.49941e-1, 8.92632e-3,
7.08416e-2, 9.63172e-1, 1.35775e-3,
2.31082e-2, 1.28021e-1, 9.36245e-1
};
float3x3 LMS_2_LIN_MAT =
{
2.85847e+0, -1.62879e+0, -2.48910e-2,
-2.10182e-1, 1.15820e+0, 3.24281e-4,
-4.18120e-2, -1.18169e-1, 1.06867e+0
};
half3 lms = mul(LIN_2_LMS_MAT, col);
lms *= balance;
return mul(LMS_2_LIN_MAT, lms);
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 c = tex2D(_MainTex, i.uv);
c.rgb = (c.rgb - 0.5f) * _Contrast + _Brightness;
c.rgb = lerp(dot(c.rgb, fixed3(0.3h, 0.587h, 0.114h)), c.rgb, _Saturation);
c.rgb *= (pow(2, _Exposure) - _Gamma) * _Color.rgb;
c.rgb = WhiteBalance(c.rgb, _Temperature_Tint.x, _Temperature_Tint.y);
return half4(c.rgb, c.a);
}
ENDCG
}
}
}
接下来改造一下颜色校正加入颜色分级和BLOOM做融合:
CS脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
[ExecuteInEditMode]
public class Tonemapping : PostEffectsBase
{
//=====================设置RTId============================
private static int rt0_id = Shader.PropertyToID(&#34;aoRT&#34;);
private static int rt1_id = Shader.PropertyToID(&#34;blurRT&#34;);
private static int rt2_id = Shader.PropertyToID(&#34;Tonemaping&#34;);
//=====================为Render.commandBuffer标识RT============
private static RenderTargetIdentifier rt0 = new RenderTargetIdentifier(rt0_id);
private static RenderTargetIdentifier rt1 = new RenderTargetIdentifier(rt1_id);
private static RenderTargetIdentifier rt2 = new RenderTargetIdentifier(rt2_id);
public Shader shader;
public Shader bloomshader;
public Material bloommat = null;
private Material mat = null;
//===========================================================================
//Bloom
//迭代次数sd
[Range(0, 15)]
[Tooltip(&#34;迭代次数&#34;)]
public int iterations = 3;
//模糊扩散范围
[Tooltip(&#34;模糊扩散范围&#34;)]
[Range(0.2f, 10.0f)]
public float blurSpread = 0.6f;
// 降频
[Tooltip(&#34;降频&#34;)]
[Range(0,4)]
public int downSample = 1;
[Tooltip(&#34;亮度阙值&#34;)]
[Range(-1.0f, 1.0f)]
public float luminanceThreshold = 0.6f;
// bloom 强度
[Tooltip(&#34;bloom 强度&#34;)]
[Range(0.0f, 255.0f)]
public float bloomFactor = 1;
[Tooltip(&#34;颜色值&#34;)]
// bloom 颜色值
public Color bloomColor = new Color(1, 1, 1, 1);
[Space(15)]
//===========================================================================
//===============================NEWBLOOM====================================
//===========================================================================
//设定渲染Pass
[Header(&#34;ToneMapping&#34;)]
[Tooltip(&#34;None=0,ACES=1,Nutral=2,Reinhard=3&#34;)]
[Range(7, 10)]
public int TonePass = 1;
//设定Tonemapping所需属性:
[Header(&#34;White Balance&#34;)]
//[Range(-100, 100)]
public Color Temperature = Color.white;
[Range(-100, 100)]
public float Tine = 0;
[Header(&#34;Color Adiustments&#34;)]
public float PostExposure = 0;
[Range(-100, 100)]
public float Contrast = 0;
public Color ColorFilter = Color.white;
[Range(-10, 10)]
public float HueShift = 0;
[Range(-100, 100)]
public float Saturation = 0;
//Channel Mixer
[Header(&#34;Channel Mixer&#34;)]
public Color ChannelMixerRed = Color.red;
public Color ChannelMixerGreen = Color.green;
public Color ChannelMixerBlue = Color.blue;
//Shadows Midtones HightLights
[Header(&#34;Shadows Midtones HightLights&#34;)]
public Color Shadows = Color.black;
public Color Midtones=Color.gray;
public Color HightLights = Color.white;
[Range(0, 1)]
public float ShadowStart;
[Range(0, 1)]
public float ShadowEnd;
[Range(0, 1)]
public float HightlightsStart;
[Range(0, 1)]
public float HightlightsEnd;
public Material material
{
get
{
mat = CheckShaderAndCreateMaterial(shader, mat);
return mat;
}
}
public Material bloommaterial
{
get
{
bloommat = CheckShaderAndCreateMaterial(bloomshader, bloommat);
return bloommat;
}
}
//自定义枚举=================================================
//==========================================================
public override void Render(RenderTexture src, RenderTexture dst)
{
if (material == null)
{
Graphics.Blit(src, dst);
return;
}
//向右位移一次。等价于 x = x / z的n次方
int width = src.width >> downSample;
int height = src.height >> downSample;
//=====================================Bloom==========================================/
//设置bloom材质属性。
bloommaterial.SetColor(&#34;_BloomColor&#34;, bloomColor);
bloommaterial.SetFloat(&#34;_BloomFactor&#34;, bloomFactor);
bloommaterial.SetFloat(&#34;_LuminanceThreshold&#34;, luminanceThreshold);
var rt0 = RenderTexture.GetTemporary(width, height, 0, src.format);
var rt1 = RenderTexture.GetTemporary(width, height, 0, src.format);
var rtbloom = RenderTexture.GetTemporary(Screen.width, Screen.height, 0, src.format);
//绘制第一个Pass
Graphics.Blit(src, rt0, bloommaterial, 0);
//设置迭代次数。
for (int i = 0; i < iterations; ++i)
{
//垂直高斯模糊
bloommaterial.SetVector(&#34;_offsets&#34;, new Vector4(0, 1.0f + i * blurSpread, 0, 0));
Graphics.Blit(rt0, rt1, bloommaterial, 1);
//水平高斯模糊
bloommaterial.SetVector(&#34;_offsets&#34;, new Vector4(1.0f + i * blurSpread, 0, 0, 0));
Graphics.Blit(rt1, rt0, bloommaterial, 1);
}
//引用一个RenderTexture设置为全局材质。
bloommaterial.SetTexture(&#34;_BlurTex&#34;, rt0);
//引用一个Color设置为全局属性。
bloommaterial.SetColor(&#34;_BloomColor&#34;, bloomColor);
Graphics.Blit(src, dst, bloommaterial, 2);
//=====================================================================================//
////=====================================NewBloom==========================================/
////设置bloom材质属性。
//var rt0 = RenderTexture.GetTemporary(width, height, 0, src.format);
//var rt1 = RenderTexture.GetTemporary(width, height, 0, src.format);
//var rtbloom = RenderTexture.GetTemporary(width, height, 0, src.format);
////绘制第一个Pass
//Graphics.Blit(src, rt0, mat, 1);
////设置迭代次数。
//for (int i = 0; i < iterations; ++i)
//{
// //垂直高斯模糊
// bloommaterial.SetVector(&#34;_offsets&#34;, new Vector4(0, 1.0f + i * blurSpread, 0, 0));
// Graphics.Blit(rt0, rt1, mat, 0);
// //水平高斯模糊
// bloommaterial.SetVector(&#34;_offsets&#34;, new Vector4(1.0f + i * blurSpread, 0, 0, 0));
// Graphics.Blit(rt1, rt0, mat, 1);
//}
////引用一个RenderTexture设置为全局材质。
////引用一个Color设置为全局属性。
//Graphics.Blit(src, dst, mat, 11);
////=====================================================================================//
//将Bloom的输出结果导入Tonemapping进行处理。
Graphics.Blit(dst, rtbloom);
Vector4 WhiteBalance = new Vector4(Temperature.r, Temperature.g, Temperature.b, Temperature.a);
Vector4 ColorAdjustments = new Vector4(PostExposure/100 + 1, Contrast/100 + 1, HueShift/100 + 1, Saturation/100+1);
Vector4 ShadowsRange = new Vector4(ShadowStart, ShadowEnd, HightlightsStart, HightlightsEnd);
mat.SetTexture(&#34;_PostFXSource&#34;, rtbloom);
mat.SetVector(&#34;_WhiteBalance&#34;, WhiteBalance);
mat.SetVector(&#34;_ColorAdjustments&#34;, ColorAdjustments);
mat.SetVector(&#34;_ColorFilterId&#34;, ColorFilter);
mat.SetColor(&#34;_ChannelMixerRed&#34;, ChannelMixerRed);
mat.SetColor(&#34;_ChannelMixerGreen&#34;, ChannelMixerGreen);
mat.SetColor(&#34;_ChannelMixerBlue&#34;, ChannelMixerBlue);
mat.SetColor(&#34;_SMHShadows&#34;, Shadows);
mat.SetColor(&#34;_SMHMidtones&#34;, Midtones);
mat.SetColor(&#34;_SMHHighlights&#34;, HightLights);
mat.SetVector(&#34;_SMHRange&#34;, ShadowsRange);
Graphics.Blit(src, dst, mat, TonePass);
//释放rt图。
//RenderTexture.ReleaseTemporary(src);
//RenderTexture.ReleaseTemporary(dst);
RenderTexture.ReleaseTemporary(rt0);
RenderTexture.ReleaseTemporary(rt1);
RenderTexture.ReleaseTemporary(rtbloom);
}
}
引用的shader:
Shader &#34;Hidden/CustomRP/PostFXStack&#34;
{
SubShader
{
HLSLINCLUDE
#include &#34;Assets/PostEffect/PostProcess/Common.hlsl&#34;
#include &#34;PostFXStackPasses.hlsl&#34;
ENDHLSL
Pass
{
Name &#34;Bloom ToneMapingNone&#34;
Blend One Zero
ZClip true
ZTest Always
ZWrite Off
Cull Off
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment ToneMappingNonePassFragment
ENDHLSL
}
Pass
{
Name &#34;Bloom ToneMapingACES&#34;
Blend SrcAlpha OneMinusSrcAlpha
ZClip true
ZTest Always
ZWrite Off
Cull Off
Blend One Zero, Zero One
//Blend [_FinalSrcBlend] [_FinalDesBlend]
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment ToneMappingACESPassFragment
ENDHLSL
}
Pass
{
Name &#34;Bloom ToneMapingNeutral&#34;
Blend One Zero
ZClip true
ZTest Always
ZWrite Off
Cull Off
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment ToneMappingNeutralPassFragment
ENDHLSL
}
Pass
{
Name &#34;Bloom ToneMapingReinhard&#34;
Blend One Zero
ZClip true
ZTest Always
ZWrite Off
Cull Off
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment ToneMappingReinhardPassFragment
ENDHLSL
}
}
}
HLSL文件:
PostFXStackPasses.hlsl
11.8K
· 百度网盘
Common.hlsl
3.3K
· 百度网盘
Tonemapping的功能写起来不是很复杂。运算方式直接取用unity本身提供的hlsl文件里的就可以了。网上也有不少可以参考的博客和大佬提供的工程。我们拿过来稍微修改修改就可以用了。最麻烦的地方可能就是和Bloom效果的混合实现了。我这里因为是分开写的所以就把BLOOM和Tomapping做了一个简单的融合。方便后期进行Bloom效果的单独优化。
在来写一个HBAO:
屏幕空间AO的效果基本是PC专用。但既然都写了这么多了就一次性全写完得了。笔者直接写的HBAO。SSAO以后有机会在弄吧。反正公式和模板啥的都很多。只要愿意找都能解决。
CS脚本和shader:
SSAO.shader
8.9K
· 百度网盘
ScreenSpaceAOEffect.cs
8.6K
· 百度网盘
具体的推导公式和实现方法可以看puppet_master的文章,我这里就不做赘述了。
Unity Shader-Ambient Occlusion环境光遮蔽(AO贴图,GPU AO贴图烘焙,SSAO,HBAO)
最后看一下实现的效果,马马虎虎。起码能做的都做了。
基本的功能都已经实现了。剩下就是怎么继续去优化和提升效果。
范例工程和预览用的Scene:(安装官方提供的后处理插件才可以正常显示。)
仅供学习用。代码都未经过优化。
伽玛.unitypackage
77.6M
· 百度网盘
总算是告一段落了。对unity本身的渲染又有了更进一步的认知。后处理是一个很宽泛的模块涉及到脚本和shader的配合使用。对于初学者会有不小的难度。但是想更进一步学习unity的渲染模块则是必须跨过去的一道门槛。
其实后处理还有很多有趣的效果比如景深,鱼眼,雨滴,噪点,损坏,破碎等等特殊的效果可以制作。甚至还可以利用commandbuffer进行渲染的扩展,进行复合效果的叠加等等。仅仅一篇文章很难进行很全面的记录。
后处理的优化也是一个难点。这里先不做过多的讨论。后续有机会在进行更进一步的记录吧。
完成日期:2022年7月7日 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|