【从UnityURP开始探索游戏渲染】专栏-直达
ChannelMixer是Unity URP后处理系统中用于颜色通道混合的核心效果组件,主要用于调整RGB通道的混合比例以实现特定的色彩分级效果。其发展历史可追溯至影视行业的传统调色技术,后被整合到Unity的Post Processing Stack中,并随着URP的演进成为Volume框架下的标准化模块
核心功能与参数
通道混合原理
通过修改输入颜色通道(Red/Green/Blue)对输出通道的贡献权重,实现如单色保留、色调偏移等效果。例如增加绿色通道对红色输出的影响会使绿色区域偏红。
ChannelMixer在Unity URP后处理中的底层原理基于颜色通道的线性代数变换,通过对RGB通道的权重矩阵运算实现色彩重构.
输出通道 = (R × R权重) + (G × G权重) + (B × B权重) + 常数偏移
该计算在片元着色器中逐像素执行,通过矩阵乘法实现颜色空间转换.
数学原理分解
- 通道变换矩阵
- 每个输出通道由3×4变换矩阵控制(包含常数项),例如红色输出通道计算为:
- R_{out}=R_{in}\\times M_{00}+G_{in}\\times M_{01}+B_{in}\\times M_{02}+M_{03}
- 其中M_03为常数偏移量。
- 亮度保持机制
- 当启用Preserve Luminosity时,系统会自动归一化权重系数,确保满足:
- M_{00}+M_{01}+M_{02}=1.0
- 防止画面过曝或欠曝
URP实现示例 关键实现解析
-
矩阵运算阶段
片元着色器通过
dot(col.rgb, _RedOut.xyz)实现向量点乘,等效于矩阵乘法。例如设置_RedOut=(0.5,0.3,0.2)时,新红色通道值为原RGB通道的50%+30%+20%混合。 -
动态参数传递
URP通过Volume组件将参数打包为Vector4(xyz为RGB权重,w为常数偏移),每帧更新至Shader。例如电影级调色常用配置:
csharpcsharp _RedOut = new Vector4(0.8f, 0.1f, 0.1f, 0);// 强化红色主基调 _GreenOut = new Vector4(0f, 1.2f, -0.2f, 0);// 增强绿色并抑制蓝色 -
后处理管线集成
RendererFeature在
BeforeRenderingPostProcessing事件点插入通道混合操作,通过双次Blit避免直接修改源纹理。临时渲染目标_TempChannelMixerTexture确保混合过程可逆. -
ChannelMixer.shader
cShader "PostProcessing/ChannelMixer" { Properties { _MainTex ("Texture", 2D) = "white" {} _RedOut ("Red Output", Vector) = (1,0,0,0) _GreenOut ("Green Output", Vector) = (0,1,0,0) _BlueOut ("Blue Output", Vector) = (0,0,1,0) } SubShader { HLSLPROGRAM #pragma only_renderers d3d11 #pragma exclude_renderers gles #pragma target 5.0 #pragma multi_compile_postprocess #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/PostProcessing.hlsl" TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); float4 _RedOut; float4 _GreenOut; float4 _BlueOut; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert(appdata v) { v2f o; o.vertex = TransformWorldToClipPos(v.vertex); o.uv = v.uv; return o; } float4 frag(v2f i) : SV_Target { float4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv); float3 result; result.r = dot(col.rgb, _RedOut.xyz) + _RedOut.w; result.g = dot(col.rgb, _GreenOut.xyz) + _GreenOut.w; result.b = dot(col.rgb, _BlueOut.xyz) + _BlueOut.w; return float4(result, col.a); } ENDHLSL } } -
ChannelMixerRendererFeature.cs
csharpusing UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class ChannelMixerRendererFeature : ScriptableRendererFeature { class CustomRenderPass : ScriptableRenderPass { Material _material; RenderTargetHandle _tempTexture; public CustomRenderPass(Material material) { _material = material; _tempTexture.Init("_TempChannelMixerTexture"); } public override void Execute(ScriptableRenderContext context, ref RenderingData data) { CommandBuffer cmd = CommandBufferPool.Get("Channel Mixer"); RenderTextureDescriptor desc = data.cameraData.cameraTargetDescriptor; cmd.GetTemporaryRT(_tempTexture.id, desc); Blit(cmd, source: "_CameraColorTexture", dest: _tempTexture.id, _material); Blit(cmd, source: _tempTexture.id, dest: "_CameraColorTexture"); cmd.ReleaseTemporaryRT(_tempTexture.id); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } [System.Serializable] public class Settings { public Material material; } public Settings settings = new Settings(); CustomRenderPass _scriptablePass; public override void Create() { _scriptablePass = new CustomRenderPass(settings.material); _scriptablePass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData data) { renderer.EnqueuePass(_scriptablePass); } }
参数说明
- Output Channel:选择要调整的目标通道(Red/Green/Blue)
- Red/Green/Blue Contribution:各输入通道对当前输出通道的影响权重(范围-2到2)
- Preserve Luminosity:保持整体亮度不变,避免过度曝光。
实现流程示例
ChannelMixerExample.cs
csharp
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class ChannelMixerExample : VolumeComponent, IPostProcessComponent {
public Vector3Parameter redOut = new(new Vector3(1, 0, 0));
public Vector3Parameter greenOut = new(new Vector3(0, 1, 0));
public Vector3Parameter blueOut = new(new Vector3(0, 0, 1));
public bool IsActive() => true;
public bool IsTileCompatible() => false;
}
ChannelMixerRenderPass.cs
csharp
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class ChannelMixerRenderPass : ScriptableRenderPass {
private Material _material;
private ChannelMixerExample _volume;
public override void Execute(ScriptableRenderContext context, ref RenderingData data) {
_volume = VolumeManager.instance.stack.GetComponent<ChannelMixerExample>();
if (_volume == null) return;
var cmd = CommandBufferPool.Get("ChannelMixer");
_material.SetVector("_RedOut", _volume.redOut.value);
_material.SetVector("_GreenOut", _volume.greenOut.value);
_material.SetVector("_BlueOut", _volume.blueOut.value);
Blit(cmd, ref data, _material, 0);
context.ExecuteCommandBuffer(cmd);
}
}
实际应用案例
电影感调色
将蓝色通道注入红色输出(如设置RedOut为(0.8, 0, 0.2)),可创造类似《黑客帝国》的青色阴影效果。
风格化渲染
通过反转绿色通道贡献(GreenOut设为(-0.5, 1, 0)),实现赛博朋克风格的色彩偏移。
黑白滤镜
统一各通道权重(如RedOut=(0.3,0.6,0.1))可生成高质量灰度图像,比直接去饱和度更可控。
胶片模拟
- 设置
BlueOut=(0,0,0.9,0.1)使蓝色通道轻微压缩,模拟柯达胶片特性
色盲辅助
- 将红色通道注入绿色输出(
GreenOut=(0.5,0.5,0,0))提升红绿色盲辨识度
风格化渲染
- 负值权重创造互补色效果,如
RedOut=(1, -0.2, 0, 0)产生赛博朋克色调
操作步骤
- 在Volume中添加Channel Mixer效果
- 调整目标通道的RGB权重三角滑块
- 启用Preserve Luminosity避免过曝
该技术现已成为URP标准管线的一部分,通过Volume系统实现非破坏性调整,比传统Shader方案更易集成到美术工作流中
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)