图形 3.2 混合模式及剔除
混合模式及剔除
什么是混合模式?
混合(Blend):混合就是把两种颜色混在一起。具体就是把某一像素位置原来的颜色和将要画上去的颜色,通过某种方式或者算法混在一起,从而实现新的效果。
假设要绘制这样一个场景:通过红色的玻璃去看绿色的物体,那么可以先绘制绿色的的物体,再绘制红色的玻璃。在绘制红色玻璃的时候,利用“混合”功能,把将要绘制上去的红色和原来的绿色进行混合,于是得到一种新的颜色。例如PS内的图片叠加、正片叠底、滤色,都属于混合模式的一种
下图把三原色通过滤色混合,从而让他们得到了新的颜色:
最终颜色 = shader计算后的颜色值 * 源因子(SrcFactor) + 累积颜色 * 目标因子(DstFactor)
累积颜色可以理解为渲染当前物体后面的颜色即GBuffer中的像素,比如你的背景,还有身后的物体。混合模式控制的就是源因子和目标因子,在脚本里会看到的就是:Blend、SrcFactor、DstFactor。主要操作的就是blend后面的源因子和目标因子。
混合模式有哪些类型?
PS混合模式的类型
ShaderLab 内的混合
- 如果颜色的某一分量超过了1.0,则它会被自动截取为1.0,不需要考虑越界的问题。
- 在所有着色器执行完毕,所有纹理都被应用,所有像素准备被呈现到屏幕之后,使用Blend命令来操作这些像素进行混合。
- 语法
- Blend Off:
- 关闭blend混合(默认值)
- Blend SrcFactor DstFactor:
- 配置并启动混合。Shader产生的颜色被乘以SrcFactor。已存在于屏幕的颜色乘以DstFactor。并且两者将被加在一起。
- Blend SrcFactor DstFactor,SrcFactorA DstFactorA:
- 同上,但是使用不同的要素来混合alpha通道。
- BlendOp Value:
- 如果使用BlendOp命令,则混合操作将设置为该值。否则,混合操作默认为Add。不是添加混合颜色在一起,而是对它们做不同的操作。
- BlendOp OpColor, OpAlpha:
- 同上,但是使用不同的操作来处理alpha通道
- AlphaToMaskOn:
- 常用在开启多重渲染(MSAA)的地表植被渲染
- Blend Off:
Blend & OpenOP
虽然所有图形API和硬件都支持混合功能,但对某些混合操作的支持较为有限。其混合等式为:
finalValue = sourceFactor * sourceValue operation destinationFactor * destinationValue
注意:任务指定渲染目标的签名都需要OpenGL4.0+、GL_ARB_draw_buffer_blend或OpenGL ES 3.2。单独的RGB和Alpha混合与高级OpenGL混合操作不兼容。
要使BlendOP生效,在同一个Pass代码块(如果该命令在Pass代码块中)或SubShader代码块(如果该命令在SubShader代码块中)中还必须有一个Blend命令。并非所有设备都支持所有混合操作,支持取决于图形API和硬件。对于不支持的混合操作,不同的图形API以不同的方式处理:GL跳过所有不支持的操作,Vulkan和Metal回退到Add操作。
总结:
- Blend命令:启用混合会禁用GPU上的一些优化(主要是隐藏表面去除Early-Z)这会导致GPU帧时间增加。
- 混合操作默认为Add
- 如果使用BlendOp命令,则混合操作将设置为该值
- 混合方程
- finalValue = sourceFactor * sourceValue operation destinationFactor * destinaValue
- finalValue : 是GPU写入目标缓冲区的值,即最终混合结果。
- sourceFactor : 在混合命令中定义,即源因子。
- sourceValue : 是片段着色器输出的值,即当前Shader内的输出值。
- operation : 是混合操作,根据是否使用blend op决定。
- destinationFactor : 在混合命令中定义,即目标因子。
- destinationValue : 是目标缓冲区已有的值,即目前的背景颜色或后面模型的颜色。
- 单独的RGB和alpha混合与高级OpenGL混合操作不兼容
常用的非高级混合类型
Blend SrcAlpha OneMinusSrcAlpha | AlphaBlending alpha混合 |
Blend One One | Additive 相加混合 |
Blend One OneMinusSrcAlpha | Soft Additive 柔和相加混合 |
Blend DstColor Zero | Multiplicative 相乘混合 |
Blend DstColor SrcColor | 2x Multiplicative 2倍相乘混合 |
Blend One OneMinusSrcAlpha | Premultiplied transparency 预乘透明度 |
混合模式的实现方式?
普通的Blend
Blend SrcAlpha OneMinusSrcAlpha - AlphaBlend alpha混合
Unity内对于Blend自带的枚举
Unity内在属性面板里面对混合命令做了一些内置的枚举,通过UnityEngine.Render.BlendOp,我们可以枚举所有的混合操作符以及混合方式。我们在做透明度混合的时候是不需要写入深度的,因此可以把ZWrite关掉。
我们在属性面板定义好之后,在SubShader中就可以把Unityform值给传入进去,因为混合操作其实并不是执行在Shader层内的,所以不要在下面的Unityform中定义。
PhotoShop 混合模式实现方式
normal
float3 Normal(float3 Src, float Dst) { Src = 0; return Dst.rgb + Src.rgb; }
alpha
描述:当前颜色 × 当前透明度 + (1 - 当前透明度) × 缓存颜色
语法:
Blend SrcAlpha
OneMinusSrcAlpha
float3 Alphablend(float4 Src, float4 Dst) { float4 C = Src.a * Src + (1.0 - Src.a) * Dst; return C.rgb; }
darken-变暗
描述:min(当前颜色,缓存颜色)×1
语法:
BlendOp Min
Blend One One
float3 Darken(float3 Src, float3 Dst) { return min(Src, Dst); }
multipy-正片叠底
描述:当前颜色 × 缓存颜色 + 缓存颜色 × 0
语法:
Blend DstColor Zero
float3 Multiply(float3 Src, float3 Dst) { return Src * Dst; }
screen-滤色
描述:当前颜色 × (1 - 缓存颜色) + 缓存颜色 × 1 或 当前颜色 × 1 + 缓存颜色 × (1 - 缓存颜色)
Src * (1 - Dst) + Dst * 1
= Src - Src * Dst + Dst
= Src + Dst - Src * Dst
语法 :
Blend OneMinusDstColor One
或
Blend One OneMinusSrcColor
float3 Screen(float3 Src, float3 Dst) { return Src + Dst - Src * Dst; }
light-变亮
描述:max(当前颜色, 缓存颜色)
语法:
BlendOp Max
Blend One One
float3 Lighten(float3 Src, float Dst) { return max(Src, Dst); }
LinearDodge-线性减淡
描述:缓存颜色 × 1 + 当前颜色 × 1
语法:Blend One One
float3 LinearDodge(float3 Src, float3 Dst) { return Src + Dst; }
ColorBurn-颜色加深
颜色加深(高级OpenGL混合)
此模式目前仅在具有
GL_KHR_blend_equation_advanced 或
GL_NV_blend_equation_advanced 扩展支持的OpenGL硬件上可用
float3 ColorBurn(float3 Src, float3 Dst) { return 1.0 - (1.0 - Dst) / Src; }
LinearBurn-线性加深
float3 LinearBurn(float3 Src, float3 Dst) { return Src + Dst - 1.0; }
DarkerColor-深色
float3 DarkerColor(float3 Src, float3 Dst) { return(Src.x + Src.y + Src.z < Dst.x + Dst.y + Dst.z) ? Src : Dst; }
LighterColor-浅色
float3 LightColor(float3 Src, float3 Dst) { return(Src.x + Src.y + Src.z > Dst.x + Dst.y + Dst.z) ? Src : Dst; }
Overlay-叠加
float overlay(float Src, float Dst) { return(Dst < 0.5) ? 2.0 * Src * Dst : 1.0 - 2.0 * (1.0 - Src) * (1.0 - Dst); }
SoftLight-柔光
float SoftLight(float Src, float Dst) { return(Src < 0.5) ? Dst - (1.0 - 2.0 * Src) * Dst * (1.0 - Dst) :(Dst < 0.25) ? Dst + (2.0 * Src - 1.0) * Dst * ((16.0 * Dst - 12.0) * Dst + 3.0) : Dst + (2.0 * Src - 1.0) * (sqrt(Dst) - Dst); } float3 SoftLight(float4 Scr, float4 Dst) { float3 C; C.x = SoftLight(Src.x, Dst.x); C.y = SoftLight(Src.y, Dst.y); C.z = SoftLight(Src.z, Dst.z); return C; }
HardLight-强光
float HardLight(float Src, float Dst) { return(Src < 0.5) ? 2.0 * Src * Dst : 1.0 - 2.0 * (1.0 - Src) * (1.0 - Dst); } float3 HardLight(float3 Src, float3 Dst) { float3 C; C.x = HardLight(Src.x, Dst.x); C.y = HardLight(Src.y, Dst.y); C.z = HardLight(Src.z, Dst.z); return C; }
VividLight-亮光
float VividLight(float Src, float Dst) { return(Src < 0.5) ? 1.0 - (1.0 - Dst) / (2.0 * Src) : Dst / (2.0 * (1.0 - Src)); } float3 VividLight(float3 Src, float3 Dst) { float3 C; C.x = VividLight(Src.x, Dst.x); C.y = VividLight(Src.y, Dst.y); C.z = VividLight(Src.z, Dst.z); return C; }
LinearLight-线性光
float3 LinearLight(float3 Src, float3 Dst) { return 2.0 * Src +Dst - 1.0; }
PinLight-点光
float PinLight(float Src, float Dst) { return(2.0 * Src - 1.0 > Dst) ? 2.0 * Src - 1.0 : (Src < 0.5 * Dst) ? 2.0 * Src : Dst; } float3 PinLight(float3 Src, float3 Dst) { float3 C; C.x = PinLight(Src.x, Dst.x); C.y = PinLight(Src.y, Dst.y); C.z = PinLight(Src.z, Dst.z); return C; }
HardMix-实色混合
float3 HardMix(float3 Src, float3 Dst) { return floor(Src + Dst); }
Difference-差值
float3 Difference(float3 Src, float3 Dst) { return abs(Dst - Src); }
Exclusion-排除
float3 Exclusion(float3 Src, float3 Dst) { return Src +Dst - 2.0 * Src * Dst; }
Subtract-减去
float3 Subtract(float3 Src, float3 Dst) { return Src - Dst; }
Divide-划分
float3 Divide(float3 Src, float3 Dst) { return Src/Dst; }
Hue-色相
float3 Hue(float3 Src, flaot3 Dst) { Dst = RGB2HSV(Dst); Dst.x = RGB2HSV(Src).x; return HSV2RGB(Dst); }
Saturation-饱和度
float3 Saturation(float3 Src, float3 Dst) { Dst = RGB2HSV(Dst); Dst.y = RGB2HSV(Src).y; return HSV2RGB(Dst); }
Color-颜色
float3 Color(float3 Src, float3 Dst) { Src = RGB2HSV(Src); Src.z = RGB2HSV(Dst).z; return HSV2RGB(Src); }
Luminosity-明度
float3 Luminosity(float3 Src, float3 Dst) { float dLum = dot(Dst, float3(0.3, 0.59, 0.11)); float sLum = dot(Src, float3(0.3, 0.59, 0.11)); float Lum = sLum - dLum; float3 C = Dst + lum; float minC = min(min(C.x, C.y), C.z); float maxC = max(max(C.x, C.y), C.z); if(minC < 0.0) return sLum + ((C - sLum) * sLum) / (sLum - minC); else if(maxC > 1.0) return sLum + ((C - sLim) * (1.0 - sLum)) / (maxC - sLum); else return C; }
剔除的实现方式
法线剔除 : 也称为背面消隐,根据法线朝向判断哪个面被剔除掉。可以用来控制是否双面渲染。
语法:Cull
后面可以接 Off Front Back
面裁剪 : clip函数会将参数小于某阈值的像素点直接在片元阶段丢弃掉。常用于制作溶解,裁剪等效果。
语法 : Clip(); 默认会切掉0.5的部分
或者使用if
总结:
1.开启双面渲染相当于绘制了两次。
2.Clip函数在某些PowerVR的机型上效率很低。
3.面裁切Clip使用AlphaTest队列
参考链接