时间:2025-08-26 14:25
人气:
作者:admin
Unity URP(Universal Render Pipeline)中的Blend和BlendOp是ShaderLab中控制颜色混合的核心指令,其发展历史与渲染技术演进密切相关。早期固定功能管线仅支持简单的Alpha混合,随着可编程着色器的普及,混合操作逐渐扩展为可定制化的数学运算。URP通过优化这些指令的底层实现,使其在移动端和高性能平台均能高效运行。
【从UnityURP开始探索游戏渲染】专栏-直达
OpenGL通过glBlendFunc和glBlendEquation函数实现混合操作,对应URP中Blend和BlendOp指令。其中:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)等价于URP的Blend SrcAlpha OneMinusSrcAlphaglBlendEquation(GL_FUNC_ADD)对应BlendOp Add操作OpenGL将混合作为固定管线阶段,在片段着色器之后执行,而URP通过可编程着色器在片元处理阶段动态控制混合参数
OpenGL 4.x支持GL_SRC1_COLOR等双源混合因子,但移动端GLES 3.0需通过扩展实现,URP对此做了平台兼容性封装
内置管线通过Queue="Transparent"标签自动启用混合,但需手动配置Blend命令且不支持BlendOp高级操作
内置管线要求显式关闭深度写入(ZWrite Off),而URP在透明渲染队列中自动管理深度测试与混合的冲突
内置管线混合计算固定于GPU固定功能单元,URP则通过可编程着色器实现动态混合策略,如根据设备性能切换Min/Max操作
| 特性 | OpenGL原生实现 | Unity内置管线 | URP优化方案 |
|---|---|---|---|
| 混合因子配置 | glBlendFunc |
Blend命令 |
封装为跨平台Shader指令 |
| 操作符扩展 | glBlendEquation |
仅支持基础加减 | 支持Min/Max/RevSub等 |
| 移动端兼容性 | 需检查扩展支持 | 全平台统一行为 | 自动降级混合方案 |
| 多渲染目标支持 | glDrawBuffers |
有限支持 | 完整MRT混合控制 |
BlendOp RevSub需调用glBlendEquation(GL_FUNC_REVERSE_SUBTRACT)Blend One One与多个Pass渲染Blend SrcAlpha OneMinusSrcAlpha等组合,解决了传统透明度混合中排序依赖和边缘锯齿问题BlendOp Max或Add操作,实现发光体叠加时的亮度累积效果BlendOp RevSub等操作允许反向颜色计算,用于特殊遮罩或腐蚀效果Blend 指令,如 Blend SrcAlpha OneMinusSrcAlpha;BlendOp 指令控制操作符,如BlendOp Sub(默认不指定是Add))混合命令是当前输出的颜色值Src,和缓冲区里的颜色值Dst做数值运算来进行混合。命令配置主要影响这个运算公式,下面先看下这个运算公式是什么样的:
$Src * SrcFactor + Dst * DstFactor$
这两个因子与颜色值相乘后相加就是新的要输出的颜色值。这里的相加是默认操作,由接下来的命令BlendOp来配置可修改。
上述公式是对颜色RGBA一起操作的配置。还有一种是分离颜色RGB和A单独控制的配置方式,与这个类似的公式:
$Src * SrcFactorA + Dst * DstFactorA$
混合的两种模板公式:
Blend SrcFactor DstFactor
Blend SrcFactor DstFactor, SrcFactorA DstFactorA
第一种配置方式直接配置RGBA,第二种将RGB和A分开配置。
那么这些Factor因子可以配置成哪些内容:
| 因子名称 | 数学表达式 | 解释 |
|---|---|---|
One |
1 | 完全保留源或目标颜色值(常用于加法混合) |
Zero |
0 | 忽略源或目标颜色值(常用于屏蔽颜色) |
SrcColor |
源颜色的RGB值 | 使用片元着色器输出的RGB值作为混合因子 |
SrcAlpha |
源颜色的Alpha值 | 使用片元着色器的透明度值(如标准透明混合) |
DstColor |
目标颜色的RGB值 | 使用帧缓冲区中已存在的RGB值(如乘法混合) |
DstAlpha |
目标颜色的Alpha值 | 使用帧缓冲区中已存在的透明度值 |
| 因子名称 | 数学表达式 | 解释 |
|---|---|---|
OneMinusSrcColor |
1 - 源颜色RGB值 | 反相源颜色值(用于特殊效果叠加) |
OneMinusSrcAlpha |
1 - 源Alpha值 | 反相源透明度(与SrcAlpha配合实现标准透明混合) |
OneMinusDstColor |
1 - 目标颜色RGB值 | 反相目标颜色值(如屏幕空间发光效果) |
OneMinusDstAlpha |
1 - 目标Alpha值 | 反相目标透明度值 |
Blend SrcAlpha OneMinusSrcAlpha(源透明×源颜色 + (1-源透明)×目标颜色)Blend One One(源颜色 + 目标颜色)Blend DstColor Zero(源颜色×目标颜色)Blend OneMinusDstColor OneMinusSrcColor(用于特殊遮罩效果)BlendOp命令
通过BlendOp修改混合计算方式,支持以下操作:
Add(默认):相加Sub:源减去目标RevSub:目标减去源Min/Max:取最小/最大值BlendOp RevSub
Blend One One // 实现颜色相减效果
使用BlendOp RevSub结合Blend One One,实现护盾边缘对背景颜色的"扣除"效果,模拟能量场扭曲
能量护盾特效通过反向减法混合(RevSub)实现背景颜色扣除,配合蜂窝纹理可产生能量场扭曲效果
EnergyShield.shader
Shader "URP/EnergyShield" {
Properties {
_MainTex ("Shield Pattern", 2D) = "white" {}
_EdgeColor ("Edge Color", Color) = (0.2,0.8,1,1)
}
SubShader {
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend One One
BlendOp RevSub
ZWrite Off
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
ENDHLSL
Pass {
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
struct Attributes {
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings {
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
half4 _EdgeColor;
Varyings vert(Attributes IN) {
Varyings OUT;
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = IN.uv;
return OUT;
}
half4 frag(Varyings IN) : SV_Target {
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
return tex * _EdgeColor;
}
ENDHLSL
}
}
}
采用Blend One One与BlendOp Add叠加多层粒子颜色,避免传统混合导致的亮度衰减
粒子系统采用加法混合(Add)确保多层粒子叠加时亮度线性累积,避免传统混合的亮度衰减问题
ParticleGlow.shader
Shader "URP/ParticleGlow" {
Properties {
_MainTex ("Particle Texture", 2D) = "white" {}
_Intensity ("Glow Intensity", Range(1,10)) = 3
}
SubShader {
Tags { "RenderType"="Transparent" "Queue"="Transparent+100" }
Blend One One
BlendOp Add
ZWrite Off
Cull Off
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
ENDHLSL
Pass {
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
struct Attributes {
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
half4 color : COLOR;
};
struct Varyings {
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
half4 color : COLOR;
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
float _Intensity;
Varyings vert(Attributes IN) {
Varyings OUT;
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = IN.uv;
OUT.color = IN.color * _Intensity;
return OUT;
}
half4 frag(Varyings IN) : SV_Target {
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
return tex * IN.color;
}
ENDHLSL
}
}
}
通过Blend SrcAlpha OneMinusSrcAlpha控制折射/反射强度,配合BlendOp Min保留深度最小的泡沫高光
水体渲染使用Min混合操作保留最小深度值,配合标准Alpha混合实现透明折射效果
WaterSurface.shader
Shader "URP/WaterSurface" {
Properties {
_MainTex ("Water Texture", 2D) = "white" {}
_NormalMap ("Normal Map", 2D) = "bump" {}
_FoamTex ("Foam Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
BlendOp Min
ZWrite Off
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
ENDHLSL
Pass {
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
struct Attributes {
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
};
struct Varyings {
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float3 viewDirWS : TEXCOORD2;
};
TEXTURE2D(_MainTex);
TEXTURE2D(_NormalMap);
TEXTURE2D(_FoamTex);
SAMPLER(sampler_MainTex);
Varyings vert(Attributes IN) {
Varyings OUT;
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
OUT.positionCS = TransformWorldToHClip(positionWS);
OUT.uv = IN.uv;
OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS);
OUT.viewDirWS = GetWorldSpaceViewDir(positionWS);
return OUT;
}
half4 frag(Varyings IN) : SV_Target {
half4 water = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
water.a = 0.7;
half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_MainTex, IN.uv));
half foam = SAMPLE_TEXTURE2D(_FoamTex, sampler_MainTex, IN.uv).r;
return min(water, foam.xxxx);
}
ENDHLSL
}
}
}
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,????)