幻影坦克架构指南(一)

幻影坦克架构纲要:

ElPsyCongree:幻影坦克架构指南(一)zhuanlan.zhihu.com图标ElPsyCongree:幻影坦克架构指南(二)zhuanlan.zhihu.com图标ElPsyCongree:幻影坦克架构指南(三)zhuanlan.zhihu.com图标ElPsyCongree:棋盘格与幻影坦克zhuanlan.zhihu.com图标

看这篇文章之前,必须强调,我是本着图形学学术的态度,不是在教人如何开车。


事情的起因是这样的,最近QQ群中老有人转发一种图片,在QQ聊天栏上是一张图片,当你点击放大后又是一张完全不一样的图片。这里我用unity 进行演示,就是 一张图片会随着背景色黑白变化,变成完全不一样的两张图片。

原图

QQ空间

当背景色为白色的时候,如图1

图1

改变相机背景色为黑色,如图2

图2

回想下 alpha混合的计算公式

Color_{合}=Color_{前}*Alpha+Color_{后}*(1-Alpha)


于是我们导出一个计算 图1(白底)与图2(黑底)的颜色方程组

已知,PNG的某个像素点的颜色值为 Color_{mix}=(r_{mix},g_{mix},b_{mix},a_{mix})

求混合白底后的图1颜色值为 Color_{1}=(r_{1},b_{1},g_{1},1) 以及混合黑底后的

图2颜色值为 Color_{2}=(r_{2},g_{2},b_{2},1)

这其实就是解一个方程组(公式1)

\begin{cases} & r_{1}= r_{mix} \times a_{mix} + (1-a_{mix})\\ & g_{1}= g_{mix} \times a_{mix} + (1-a_{mix})\\ & b_{1}= b_{mix} \times a_{mix} + (1-a_{mix})\\ & \\ & r_{2}= r_{mix} \times a_{mix}\\ & g_{2}= g_{mix} \times a_{mix}\\ & b_{2}= b_{mix} \times a_{mix}\\ \end{cases}

Color_{mix} 的值代入即可求出 Color_{1}Color_{2}


验证下想法,把 shader的alpha混合关了,然后分别计算,得到结果。


图1的效果

图2的效果

(这里注意,我已经在黑底下,显示了开启alpha通道白底的效果;在白底下显示了开启alpha通道黑底的效果。)

关键的Shader代码

fixed4 frag (v2f IN) : COLOR {
      fixed4 color = tex2D(_MainTex, IN.uv);// *IN.color;
      fixed a = color.a;
      
      //这里是白底
      //fixed r = color.r * a + (1 - a);
      //fixed g = color.g * a + (1 - a);
      //fixed b = color.b * a + (1 - a);
      
      //这里是黑底
      fixed r = color.r * a;
      fixed g = color.g * a;
      fixed b = color.b * a;
      color = fixed4(r, g, b, 1);

      return color;
}

好的通过一张带 alpha通道的图片根据背景色不同分解成两张图片的原理已经说明了,我不经要想,如果我有两张不带alpha通道的等长等高图片如何合成一张可根据背景色分解的图片啊?

于是数学问题发生了转变

已知白底的图1颜色 Color_{1}=(r_{1},b_{1},g_{1},1) ,以及黑底的图2颜色 Color_{2}=(r_{2},b_{2},g_{2},1) ,求带alpha通道的颜色 Color_{mix}=(r_{mix},g_{mix},b_{mix},a_{mix})

同理我们的公式1仍然适用这个模型,回想下公式1

\begin{cases} & r_{1}= r_{mix} \times a_{mix} + (1-a_{mix})\\ & g_{1}= g_{mix} \times a_{mix} + (1-a_{mix})\\ & b_{1}= b_{mix} \times a_{mix} + (1-a_{mix})\\ & \\ & r_{2}= r_{mix} \times a_{mix}\\ & g_{2}= g_{mix} \times a_{mix}\\ & b_{2}= b_{mix} \times a_{mix}\\ \end{cases}

对这个方程组进行求解得到:

\begin{cases} & a_{mix}= 1-r_{1} +r_{2}\\ & a_{mix}= 1-g_{1} +g_{2}\\ &a_{mix}= 1-b_{1} +b_{2}\\ & \\ & r_{mix}= r_{2} /( 1-r_{1}+r_{2})\\ & g_{mix}= g_{2} /( 1-g_{1}+g_{2})\\ & b_{mix}= b_{2} /( 1-b_{1}+b_{2})\\ \end{cases}

这里有两个问题

第一个也是最大的问题, a_{mix} 有3个表达式,并且这3个表达式必须全部相等。而正常的一张五颜六色的图片很难出现这种情况,所以为了正常显示一张图片的大概,最好的方式就是将其转为灰度图,也就是 r_{1}=g_{1}=b_{1},r_2=g_{2}=b_{2} ,很容易验算 a_{mix} 三个表达式相等。

于是为了方便我把方程组的解省略为:

\begin{cases} & a_{mix}= 1-r_{1} +r_{2}\\ & r_{mix}= r_{2} /a_{mix}\\ \end{cases}

第二个问题,如果是灰度图,那么对每个像素 a_{mix}\leq 1 也就是 r_{1}\geq r_{2} ,因为alpha值的值域就是[0,1],如果超过就不正常。处理的做法就是将 r_{2} 缩小一定的比例(图像显示上就是变暗),因为对 r_{1} 颜色进行放大容易超过1,从而导致 颜色信息丢失。


既然验证了思想我就来做实际行动吧,我随便上崩坏3截了两张图

图3


图4

关键Shader代码(要开启alpha 混合,并且是正常叠加)

fixed4 frag (v2f IN) : COLOR {
    fixed4 color1 = tex2D(_MainTex, IN.uv);
    // 转灰度图
    color1.rgb = dot(color1.rgb, fixed3(.222,.707,.071));

    fixed4 color2 = tex2D(_BackTex, IN.uv);
    // 转灰度图,因为是黑色背景要使其变暗
    color2.rgb = dot(color2.rgb, fixed3(.222, .707, .071)) * 0.3;

    fixed a = 1 - color1.r + color2.r;
    fixed r = color2.r / a;
    fixed4 color = fixed4(r, r, r, a);

    return color;
}

运行起来的效果

白底

黑底

编辑于 2019-08-04

文章被以下专栏收录