【笔记】【百人计划】图形3.2 混合模式及剔除

一、什么是混合模式

  • 混合
    • 就是两种颜色混在一起。具体就是把某一像素位置原来的颜色和将要画上去的颜色,通过某种方式混在一起,从而实现新的效果
  • 透过红色玻璃看绿色玻璃
    • 可以先绘制绿色玻璃,再绘制红色玻璃。在绘制红色的时候,利用混合功能,把将要绘制上去的红色和原来的颜色(绿色)进行混合,于是得到新的颜色
    • 也可以理解成,绿色首先对背景施加影响,无论红色玻璃存在与否,这个影响都是存在的,所以首先计算后面的绿色。然后再考虑前面的红色的影响。这也是我理解透明物体从后往前绘制的原理的一种方式。

最终颜色=Shader计算后的颜色值*源因子(SrcFactor)+累计颜色*目标因子(DstFactor)

二、混合模式的类型

PS中的混合模式

image-20220817000953004

ShaderLab内的混合

  1. 如果颜色的某一分量超过1.0,则会被自动截取位1.0,不需要考虑越界的问题。

  2. 再所有着色器执行完毕,所有纹理都被应用,所有像素准备被呈现到屏幕之后,使用Blend命令来操作这些像素混合。

  3. 语法

    语法
    Blend Off 关闭blend混合(默认)
    Blend SrcFactor DstFactor 配置并启动混合计算
    Blend SrcFactor DstFactor, SrcFactorA DstFactorA 同上,但是使用不同的要素来混合Alpha通道
    BlendOp Value 如果使用BlendOp命令,则混合操作将设置为该值。否则,混合操作默认为Add。
    BlendOp OpColor, OpAlpha 同上,但是使用不同的操作来处理alpha通道
    AlphaToMaskOn 常用于开启MSAA的地表植被的渲染

Blend和BlendOp

1
2
3
4
5
6
7
finalValue = sourceFactor * sourceValue operation destinationFactor * destinationValue
//finalValue: GPU写入目标缓冲区的值
//sourceFactor: Blend命令中定义
//sourceValue: 片元着色器输出的值
//operation: 混合操作
//destinationFactor: Blend命令中定义
//destinationValue: 目标缓冲区现有值的值
  • 可以写在Pass中或SubShader中(BlendOp在同一个代码块中还必须有一个Blend命令)
  • 启用混合会禁用GPU上的一些优化(主要是隐藏表面去除Early-Z)

三、混合模式的实现方式

Unity附带的Blend枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//混合模式
[Enum(UnityEngine.Rendering.BlendOp)] _BlendOp ("BlendOp", float) = 0
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("SrcBlend", float) = 1
[Enum(UnityEngine.Rendering.BlendMode)]
_DstBlend ("DstBlend", float) = 0

//深度开关
//ZWriteMode没有内置,只有两种状态,也可以用Toogle
[Enum(Off, 0, On, 1)] _ZWriteMode ("ZWriteMode", float) = 1


SubShader{
Tag {"RenderType" = "Transparent" "Queue" = "Transparent"}
ZWrite [_ZWriteMode]
Blend [_SrcBlend] [_DstBlend]
//这部分定义是CPU阶段的渲染设置,并不传入shader,不用定义uniform
}

Photoshop的Blend实现方式

自己查吧

  • 普通的Blend
    • Blend SrcAlpha OneMinusSrcAlpha
      • //Alpha混合 Alpha Blending
  • 变暗Darken
    • BlendOp Min
    • Blend One One
    • min(当前颜色,缓存颜色)*1
  • 正片叠底
    • Blend Discolors Zero
    • 当前颜色*缓存颜色+缓存颜色*0
  • 滤色Screen
    • Blend OneMinusDstColor One 或Blend One OneMinusSrcColor
    • Src*(1-Dst)+Dst*1 = Src+Dst-Src*Dst
    • 当前颜色*(1-缓存颜色)+缓存颜色*1 或 当前颜色*1 + 缓存颜色* (1-缓存颜色)
  • 变亮Lighten
    • BlendOp Max
    • Blend One One
    • max(当前颜色,缓存颜色)
  • 线性减淡LinearDodge
    • Blend One One
    • 缓存颜色*1+当前颜色*1
  • 颜色加深ColorBurn
    • 高级Opengl混合
    • 此模式目前仅在具有GL_KHR_blend_equation_advanced或GL_NV_blend_equation_advanced扩展支持的Opengl硬件上可用
    • 1-(1-Dst)/Src
  • 。。。

四、剔除的实现方式

  • 法线剔除
    • 也称背面消隐,根据法线朝向判断哪个面被剔除掉,可以用来控制是否双面渲染
    • Cull + [Off, Front, Back]
  • 面裁切
    • Clip函数会参数小于某像素点直接在片元阶段丢弃,常用于制作溶解,裁剪等效果
    • Clip();//默认会切掉0.5的部分,或者使用if
1
2
3
if(input.posInObjectCoords.y > 0.5) discard;
//剔除模式
[Enum(UnityEngine.Rendering.CullMode)] _CullMode ("CullMode", float) = 2

总结

  • 开启双面渲染相当于绘制了两次
  • Clip函数在某些PowerVR的机型上效率低
  • 面裁切Clip使用AlphaTest队列

作业

实现常用的混合模式,并设计使用界面

参考资料

[1] https://www.bilibili.com/video/BV1sL4y1v7SS