图形 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 & 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)
{
    return2.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队列

 


参考链接

 

【技术美术百人计划】图形 3.2 混合模式及剔除

知乎-一篇文章彻底搞清PS混合模式的原理

简书-PS中27种图层混合模式原理详解

 

跳转回百人合集

posted @ 2022-03-12 18:40  anesu  阅读(244)  评论(0编辑  收藏  举报