搜索

在 html 中用加色法混合颜色

gecimao 发表于 2019-06-11 20:18 | 查看: | 回复:

  本文通过解决一个假想的问题介绍了 css screen 混合模式,并介绍了如何用 svg 滤镜、canvas 2d、canvas webgl 实现相同的效果。

  观察重叠部分发现该部分的颜色不仅受自己的影响、还受它下面背景颜色的影响,重叠部分的颜色是自己的颜色和背景颜色混合的结果。换句话说,一个像素绘制出来的颜色等于像素颜色和背景像素颜色的混合,即

  下面给上面的式子代入几组实际值。设\(C_s\)是不透明红rgba(1, 0, 0, 1),\(C_b\)是不透明蓝rgb(0, 0, 1),它俩混合的结果不用计算都知道仍然是不透明红,计算过程如下,

  回过头来观察式子\(C_s \times α_s + C_b \times (1 - α_s)\),可以看出结果介于\(C_s\)和\(C_b\)之间。红绿蓝混合时,白色的红色分量只能通过红色得到,这要求红色的 α 是 1,但 α = 1 造成背景颜色蓝或者绿被忽略,而忽略任何一个分量都无法得到白色。因此这个混合函数不合适。

  如果可以自己逐一计算像素的颜色,得出要求的效果自然不在话下。除了自己计算外,如果存在正好能够实现要求效果的固定函数,则调用该函数也可以。

  css 有个模块叫复合与混合,这个模块定义了若干固定函数,其中一个叫 screen,它的\(B\)是

  假设 add 是\(min(C_s + C_b, 1)\),screen 虽然不是 add 但是也可以把红绿蓝合成白色,实现要求的效果。至于 add、screen 或其它混合函数哪个能更精确地反映光线的混合,我也搞不清楚。

  本文把 svg 写在 html 内 。svg 是 xml 应用程序,遵循 xml 语法,但是放在 html 中又可以采用部分 html 语法。如果大家按照 xml svg 的知识去看本文的代码可能会有疑问,所以在写 svg 之前先说一下 html 中的 svg。

  所有没有内容的 xml 元素都叫empty 元素,可以自闭合;html 不存在 empty 元素,但是定义了一些void 元素,void 元素不能有内容,只有开始标记没有结束标记。

  有些元素可以省略结束标记,但不是 void 元素,比如li;有些元素有时候没有内容,但既不是 void 元素也不能省略结束标记,比如script src=xxx;有些元素可以省略开始标记。

  另外,svg 很多要素都没有浏览器支持;当支持的时候,可能各个浏览器有差异。

  canvas 分为 2d 和 webgl,它里面的形状都是画上去的,由像素组成,不是 dom 元素,无法应用 css 混合;但是 canvas 2d 有个全局复合操作,和 css 混合是同一个概念在两种不同语言中的实现,支持 css 混合的所有固定函数。当然自己计算像素也行。

  webgl 没有与 css、canvas 2d 完全相同的混合概念,但也有自己的混合函数,解决本文提出的问题不在话下。webgl 有个特点是无论你干什么都需要写着色器代码、写调用编译着色器的函数的代码、写调用连接着色器的函数的代码。

  下面是框架代码,后面给出的示例代码需要放在框架代码的body里

  元素的mix-blend-mode属性是说,我知道你的颜色,但是在显示的时候不要只显示你的颜色,而是要显示你的颜色和你背景颜色混合后的颜色,至于如何混合,我会通过mix-blend-mode属性的值指出。

  上面的代码实现了刚才的设计,并且额外设置了容器 div 的一个属性isolation: isolate。元素的isolation: isolate是说,我的子元素不会和我外面的元素混合。isolation属性另外一个可能的取值兼默认值是auto,没有限制、随便混和。

  绿蓝方块和白色混合也得到白色,结果就是一片白,不是要求的效果,所以设置容器的isolation: isolate。

  简而言之出于性能考虑伪输入图像BackgroundImage是背景的一个快照,要使用BackgroundImage作in或in2的参数必须指定容器元素的enable-background=new以要求容器存储背景快照供滤镜使用。enable-background默认值是accumulate,不存,也无法使用BackgroundImage。两个不同的单词可能是要强调输入图像和背景图像的这种差异。当然也可能是我想多了,人家就是喜欢出其不意,如之奈何?

  这是我心里想象的 ie 工作人员心里的想象,请不要以为他们一定是那样想的。

  这么看来 ie 是被坑了,但从大的方面看 svg 本身就很坑,废弃enable-background一点都不亏。svg 很好玩,但是能别用 xml 语法吗?

  一般只要 chrome 和 firefox 能用,ie 我常常忽略,前面 css 的示例代码就没管 ie。现在这段代码,由于只有 ie 10+ 支持伪输入图像BackgroundImage,chrome 和 firefox 上都运行不了,不能说是达到了要求的效果,最好有在 3 个浏览器上都能运行的 svg 例子。所幸对于这个简单的问题,TIMTOWTDI!下面凑一个能在 3 个浏览器上运行的 svg 解法,说凑是因为它没有把 3 个元素两两混合,而是在滤镜里生成了两个方块,和应用滤镜的那个方块元素混合。在滤镜里生成方块指的是把滤镜矩形设置为单一的颜色然后临时保存,这要用到feColorMatrix,

  说的是当把图像传递给feColorMatrix时,对图像的每一个像素左乘下面的矩阵以得到新的像素,

  也可以做红绿蓝 3 张图片,在滤镜里用feImage引用,代码类似下面,记得先做 3 张 100px * 100px 的红绿蓝图片放到 html 的同一目录。

  svg 滤镜的思路就是feComposite和feBlend,前者自己计算像素,后者调用固定函数。由于 chrome 和 firefox 不支持在滤镜中读取背景图像所以给了两段绕弯的代码,第 2 段代码还依赖 3 张图片。

  不能直接在代码里使用CanvasRenderingContext2D.prototype.globalCompositeOperation,因为这句代码会调用get函数,而get需要通过this访问实际的画布上下文对象。当然正常情况下也不会那么写,正常情况是先获取某个画布的 2d 上下文,然后访问上下文的属性,

  因为红绿蓝两两混合时每个分量必然一个是 0 一个是 1,所以并没有调用 Math.min 而是直接把分量置 1,或者说置 255。

  本节的目标是在阅读本节内容之后,对 webgl 一无所知的读者能掌握 webgl 的基本思路、写出基本的 webgl 程序。如果不是这个情况,请跟帖指出,我会修正本节内容直至达到前述目标。

  1 个 webgl 程序可以有很多着色器程序,每个着色器程序一定有 1 个顶点着色器和 1 个片段着色器。每绘制 1 个点,webgl 都依次调用顶点着色器和片段着色器。顶点着色器的唯一任务是给全局变量gl_Position赋值,它代表 1 个点的位置;片段着色器的唯一任务是给全局变量gl_FragColor赋值,它代表刚才那个点的颜色。

  webgl 实际上读取的是从 javascript 数组拷贝到显卡上的数组

  读作:为了给顶点着色器里面定义的第index号特性赋值,从数组中取stride个字节作为一个顶点,从这个顶点的第offset个字节开始取size个type,每个type依次对应特性的一个分量。

  每次进入顶点着色器的时候都希望这俩变量被赋值,以便在顶点着色器的main里面使用它们。在 javascript 里面用一个Float32Array保存顶点,数组形如

  每个顶点的画布分辨率resolution都一样,所以一般不这么传递,放在这里只是为了举例。

  为了绘制一个方块,调用gl.drawArrays(gl.TRIANGLE_FAN, first, 4);,意思是从当前绑定的数组的第first个顶点开始用连续的 4 个顶点组成 1 个三角扇,这 4 个点的位置是事先规划好的,排列如下

  有了这些知识下面写一个绘制黑色方块的程序,里面出现了顶点着色器和片段着色器代码,

  我假设读者通过前面的阅读和练习已经理解了gl.drawArrays和gl.drawElements,下面简要介绍设置颜色。

  前面在片段着色器里硬编码了个颜色,不透明黑,要画 3 个颜色的方块就需要 3 个片段着色器。如果能让片段着色器接受一个 javascript 传入的变量,有点像顶点着色器里面的特性attribute,从 javascript 指定颜色,那就可以只写一个片段着色器。出于两个原因,不使用attribute

  这里使用uniform。varying用来从顶点着色器往片段着色器传值,当然也可以实现效果。

  uniform的意思是,每次调用gl.drawArrays绘制一系列的顶点之前,先设置一个在绘制过程中保持不变的值,绘制这些点的过程中,着色器程序可以读取但不能修改该定值。对比attribute,attribute对gl.drawArrays绘制的每 1 个顶点都赋值 1 次;uniform只在gl.drawArrays开始前赋值一次。

  后面的示例中,顶点着色器定义 1 个attribute以接受顶点,片段着色器定义 1 个uniform以接受颜色,调用 3 次gl.drawArrays以绘制 3 个方块。

  我在 csdn 用 markdown 编辑器发表了同名文章,发表后发现编辑时预览的格式和发出去后的格式不太一样,每次修改还都要审核很久,所以就放到 cnblogs 看了看。markdown 编辑器方面 cnblogs 比 csdn 好的地方在

本文链接:http://saskatoonflowers.net/dingdianhunhe/462.html
随机为您推荐歌词

联系我们 | 关于我们 | 网友投稿 | 版权声明 | 广告服务 | 站点统计 | 网站地图

版权声明:本站资源均来自互联网,如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

Copyright @ 2012-2013 织梦猫 版权所有  Powered by Dedecms 5.7
渝ICP备10013703号  

回顶部