什么是Dither

基本概念

  • v. 犹豫不决;对(录音)进行噪声处理;抖色
  • n. 犹豫不决;紧张,颤抖

用有限的颜色来表示出多种色阶变化的技术就叫做Dither

上面那句话其实并不严谨,只是在此方便理解,Dither不是新型技术,它在视频,信号处理,音频等领域都有应用

图形中的应用

基于上述的简单且并不严谨的描述,下图是在一个2x2的像素格中,从左到右依次排列出一个黑白色阶的效果

对应黑白色阶

没看出来?我们将像素格缩小平铺

这下就对味了!

现在我们对Dither在图形上的应用已经有了初步的了解,并且我们可以看得到,2x2的像素格只有5种颜色过渡,那么4x4呢?

  • 2x2像素格有5种颜色
  • 4x4像素格有17种颜色
  • 8x8像素格有65种颜色

有序抖动与无序抖动

这里我们不涉及更深的底层逻辑,直观一些,直接看图

这张图展示了不同算法呈现出的结果,其中下面

这张,就是有序Dither的效果,也是我们下面要实现的效果(有点像素画的感觉哈)

Dither的实现

这里我们的主要目标是先绘制一个2x2像素格的矩阵,然后再想办法将这些像素格填充平铺到屏幕中去

有了目标,下面我们开干!

像素格矩阵

首先我们想要绘制下面这样一个2x2的像素格,对应的矩阵应该是什么样的?

显而易见

平铺到屏幕中

方法其实很简单,只需要将上面那个2x2矩阵作为一个贴图一样目标,再用0,1,0,1,0,1不断重复的坐标值采样它就行了

说起来有点抽象,首先我们先看采样它的值从哪来

i.positionCS.xy

没错,就是它,我们知道裁剪空间下的坐标值,从顶点着色器输出到片段着色器后,它的值就是屏幕上每一个像素的值

且值非常大,不是简单的0-1,而且我们的矩阵只能取到0和1,即0行0列、0行1列.....所以必须将这些值映射为0和1,只需要除以2取余即可

直接输出 i.position.y - 400

所以我们就是用它的值,来采样2x2像素矩阵(与其说是采样,不如说是2x2像素矩阵的下标)

下面我们用代码实现一下

[4]先声明一个4x4的矩阵,我们知道这种排列方式是一个中灰色

[10]声明uint2二维整数类型,再将屏幕坐标值除以2取余

[12]通过屏幕坐标的x轴与y轴,查找矩阵的值

half4 frag(Varyings i) : SV_TARGET {    float2x2 M2x2 = float2x2(0,1,1,0);uint2 uv = (uint2)i.positionCS.xy%2;return M2x2[uv.x][uv.y];}

最终效果

放大看

效果出现了,我们还可以改变矩阵的排列方式,呈现出不同的颜色状态

  half4 frag(Varyings i) : SV_TARGET {    float2x2 M2x2 = float2x2(0,1,0,0);uint2 uv = (uint2)i.positionCS.xy%2;return M2x2[uv.x][uv.y];}

黑色会更多

优化代码实现

上面我们已经知道了核心代码如何实现,因为还需要用到不同的矩阵数量以及排列方式(颜色),为了方便,我们直接创建自己的方法便于使用

             // 2x2矩阵half Dither2x2_Matrix(uint2 uv ){uv %= 2;float2x2 M2x2 = float2x2(0,1,0,0);return M2x2[uv.x][uv.y];}half4 frag(Varyings i) : SV_TARGET {    uint2 uv_gray = (uint2)i.positionCS.xy;half4 c = Dither2x2_Matrix(uv_gray);return c;return 1;}

趁热再写一个4x4的

  // 4x4矩阵half Dither4x4_Matrix(uint2 uv ){uv %= 4;float4x4 M4x4 = float4x4(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return M4x4[uv.x][uv.y];}half4 frag(Varyings i) : SV_TARGET {    uint2 uv = (uint2)i.positionCS.xy;half4 c1 = Dither4x4_Matrix(uv);return c1;}

4x4矩阵采样的效果

这时候我们发现,我们写的4x4矩阵明明是从左往右的斜线呀,为什么显示出来后,是相反的?

主要是因为y轴,屏幕的坐标是以左下角为(0,0)点(OpenGL),但逐行采样矩阵时,却是从左上角开始的,所以就导致了这种情况发生

矩阵转化为数组

像素格元素不但可以用矩阵表示,也可以声明数组,不同的是,矩阵我们可以指定行与列,数组如何指定?

我们只需要用另一种算法作为数组的下标

例如3x3的矩阵

我们可以指定其几行几列,例如2行1列(从0开始),是7

数组

如果用上面那个算法呢?

注意,这里的值仅仅是它们的下标,或者索引值,数组或矩阵内部的值当然可以所以更改

但它们所指定的某一个数是一致的

它们的写法有所不同

             // 4x4数组half Dither4x4_Array(uint2 uv ){uv %= 4;float A4x4[16]={1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};return A4x4[uv.x*4+uv.y];}half4 frag(Varyings i) : SV_TARGET {    uint2 uv = (uint2)i.positionCS.xy;half4 c1 = Dither4x4_Array(uv);return c1;}

Dither有序抖动

什么是有序抖动?

其实就是利用这样的矩阵,但看起来很乱不是吗?

它有一个规律,就是每两个数字之间都会相差较大,仔细看,还真是这样

下面我们就模仿这个矩阵的写法,创建一个出来

 // 8x8数组half Dither8x8_Array(uint2 uv ){uv %= 8;float A4x4[64]={0,32,8,40,2,34,10,42,48,16,56,24,50,18,58,26,12,44,4,36,14,46,6,38,60,28,52,20,62,30,54,22,3,35,11,43,1,33,9,41,51,19,59,27,49,17,57,25,15,47,7,39,13,45,5,37,63,31,55,23,61,29,53,21};return A4x4[uv.x*8+uv.y];}

等等

数组里这些数值,除了0以外,其他的超过1的不都是白色?

别担心,我们可以用除法将他们映射到0-1区间,除以64看看

// 8x8数组half Dither8x8_Array(uint2 uv ){uv %= 8;float A4x4[64]={0,32,8,40,2,34,10,42,48,16,56,24,50,18,58,26,12,44,4,36,14,46,6,38,60,28,52,20,62,30,54,22,3,35,11,43,1,33,9,41,51,19,59,27,49,17,57,25,15,47,7,39,13,45,5,37,63,31,55,23,61,29,53,21};return A4x4[uv.x*8+uv.y]/64;}half4 frag(Varyings i) : SV_TARGET {    uint2 uv = (uint2)i.positionCS.xy;half4 c = Dither2x2_Matrix(uv);return c2;}

这样我们就获得颜色不均的有序Dither效果了

有序抖动Dither的应用

有啥用??????

先给出答案:我们可以配合Clip方法用它来做半透明效果,也就是通过AlphaTest达到半透明效果

相信你已经知道改怎么写了

             // 8x8数组half Dither8x8_Array(uint2 uv ){uv %= 8;float A4x4[64]={0,32,8,40,2,34,10,42,48,16,56,24,50,18,58,26,12,44,4,36,14,46,6,38,60,28,52,20,62,30,54,22,3,35,11,43,1,33,9,41,51,19,59,27,49,17,57,25,15,47,7,39,13,45,5,37,63,31,55,23,61,29,53,21};return A4x4[uv.x*8+uv.y]/64;}half4 frag(Varyings i) : SV_TARGET {    uint2 uv = (uint2)i.positionCS.xy;half4 c = Dither2x2_Matrix(uv);clip(c2 - _Clip);return c2;}

这样我想到了小米透明电视。。。

放大看看

嘿!像不像MineCraft的玻璃!

像素风格

有序Dither不但可以作为廉价的半透明实现,最明显的作用就是将图像变成像素风格

实现原理

将目标图像(贴图,或者光照等)也传入Dither矩阵的方法中去,并将目标图像的数值与Dither矩阵中的数值做对比(Step/SmoothStep),然后输出即可

代码实现

[18]使用Step方法将有序Dither 与贴图颜色进行对比

[27]将RGB颜色转换为灰度的方法

 // 8x8数组half Dither8x8_Array(uint2 uv , float color){uv %= 8;float A4x4[64]={0,32,8,40,2,34,10,42,48,16,56,24,50,18,58,26,12,44,4,36,14,46,6,38,60,28,52,20,62,30,54,22,3,35,11,43,1,33,9,41,51,19,59,27,49,17,57,25,15,47,7,39,13,45,5,37,63,31,55,23,61,29,53,21};half pixel = A4x4[uv.x*8+uv.y]/64;return step(pixel,color);}half4 frag(Varyings i) : SV_TARGET {    half4 mainTex = SAMPLE_TEXTURE2D(_MainTex,smp,i.uv);half maintexGray = Luminance(mainTex.rgb);uint2 uv = (uint2)i.positionCS.xy;half4 c2 = Dither8x8_Array(uv,maintexGray);return c2;}

有点意思了

但是现在只是黑白的,毕竟受该方法的限制,我们不能一次性传入RGB进行对比

但是我们可以对每个通道比较最后再合成到一起!

 half4 frag(Varyings i) : SV_TARGET {    half4 mainTex = SAMPLE_TEXTURE2D(_MainTex,smp,i.uv);uint2 uv = (uint2)i.positionCS.xy;half c2 = Dither8x8_Array(uv,mainTex.r);half c3 = Dither8x8_Array(uv,mainTex.g);half c4 = Dither8x8_Array(uv,mainTex.b);half4 c = half4(c2,c3,c4,1);return c;}

效果一般,真正使用的话,还是需要再做修改

Houdini生成Dither纹理

使用Houdini可以将不同类型的Dither矩阵纹理填入同一张贴图的通道中

这里稍复杂,但节点不多,首先是B通道中填充8x8有序DIther

注意grid节点,需要先把网格设置为8x8的大小,这样每个面都为1个单位,并且x,z的位置也设定好,使得左上角为(0,0)点

Wrangle中的代码

注意Run Over设置为面,我们要取每个面的数值,并使他们为从0到63的排列方式,作为有序Dither矩阵的索引

最后一行输出到b通道

maps_baker节点

输出后的贴图B通道

URP管线下使用Dither做像素化风格相关推荐

  1. Unity URP管线下多光源渲染

    抓手 urp管线下,获得其他多光源的方式和内置管线的不一样. 本文会阐述具体方法,并给出源码. 具体步骤 首先要在pipeline中打开Additional lighting的设置. 然后在shade ...

  2. Unity Shader - URP Fog - URP 管线下的雾效

    文章目录 参考 LitForwardPass.hlsl 临摹使用 Test/URPFog 只要 Fog_Linear 变体的 效果 问题 修复 References 管线:URP URP:7.7.1 ...

  3. Unity在URP管线下使用TriLib插件加载模型材质不正确的问题

    目前使用Unity开发项目绝大部分已经使用URP渲染管线,但是TriLib加载进来的模型默认的还是使用内置渲染管线的材质,这会导致材质无法正常显示,解决办法如下: 1.在Assets目录下新建一个As ...

  4. AirPlane Race Creator竞速游戏完整项目自定义模型操作说明基于Urp管线

    最近一段时间,脑子被门夹了,伤心病狂地去搞引擎去了,之后会回归理性,重新做一些Unity插件的入门讲解 今天讲解的是,完整项目:AirPlane Racer - URP 特别适合某些人学习: 策划 - ...

  5. unity urp管线扫光效果

    urp管线下实现扫光效果 基本思路,还是根据深度还原世界坐标(以下简称world_pos),根据坐标的xyz判断是否在一些范围内,然后进行基于xyz两个坐标轴进行扫光 这里进行一个扩展,让扫光可以沿着 ...

  6. 如何在Unity中自定义光源,包含URP管线和Build in 管线(一)

    如何在Unity中自定义光源,包含URP管线和Build in 管线(一) 众所周知,光照在游戏画面效果上占了很大比例,一个游戏画面好不好,用最简单的理解来说,就是看游戏画面亮不亮,当然这个亮不是不是 ...

  7. Linux下使用WPS做office的二次开发

    Linux下使用WPS做office的二次开发 序 上个版本WPS在Linux上就已经支持二次开发了,可以直接去看官网相关的介绍.https://open.wps.cn/ 我们选择WPS的客户端进行二 ...

  8. 院士:科研工作者也得养家!非升即走压力下,不得不做短平快的研究

    点击下方卡片,关注"CVer"公众号 AI/CV重磅干货,第一时间送达 点击进入-> CV 微信技术交流群 本文转载自:募格课堂 | 综合自上游新闻.澎湃新闻.百度百科 如今 ...

  9. Unity URP管线的PBR材质及Tessallation Shader(Height Map高度贴图)

    在使用URP管线的过程中发现默认的URP管线的shader是没有提供height map参数设置的,经过查找才知道URP管线中height map相关的功能需要自己写shader开启Tessallat ...

  10. Unity - 搬砖日志 - BRP 管线下的自定义阴影尺寸(脱离ProjectSettings/Quality/ShadowResolution设置)

    文章目录 环境 原因 解决 CSharp 脚本 效果预览 - Light.shadowCustomResolution 效果预览 - Using Quality Settings 应用 Control ...

最新文章

  1. 说说你对 SVG 理解?
  2. Python学习笔记:Day5 编写web框架
  3. [vue] 怎么缓存当前打开的路由组件,缓存后想更新当前组件怎么办呢?
  4. 电脑突然卡主动不了了_必看!电脑运行卡或软件卡死无响应,怎么办?
  5. 春晚“宕机”魔咒失效 火山引擎助抖音成功闯关
  6. 无法删除文件:无法读源文件或磁盘”
  7. nginx 基本入门
  8. nginx反向代理服务器
  9. premiere调色预设怎么用
  10. vscode代码自动保存插件_VSCode 云同步扩展设置 Settings Sync 插件
  11. P2015 二叉苹果树 树形DP
  12. 重装系统“无法创建新的分区也找不到现有分区”
  13. 数据结构课程设计(部分选题)
  14. linux centos java kumo图片合成文字 词云插件 字体乱码问题
  15. 【vue eslint】报错Component name “xxxxx“ should always be multi-word.eslintvue/四种解决方案
  16. http请求 状态码204
  17. 《Arduino开发实战指南:LabVIEW卷》6.6 基于Arduino的弹珠游戏
  18. 有关逻辑炸弹方面的问题
  19. phpcms 推荐位获取
  20. 星载合成孔径雷达导论——合成孔径雷达概述

热门文章

  1. CTGU 2021春-MySQL数据库实验2:基本查询1-2关,共10小题全代码+信息表+通关截图!
  2. java实现百度网盘爬虫
  3. 【debug】Support for password authentication was removed on August 13, 2021.解决
  4. 2017前端开发手册三-前端职位描述
  5. SH-SSS丨CUSIDE:分块、模拟未来、解码的流式语音识别新框架
  6. U8文件服务器备份,u8备份缓存文件在哪
  7. Android调用自带TTS文本转语音引擎实现离线语音合成
  8. 简单明了的java反射机制
  9. 达梦数据库ZYJ实例安装初始化
  10. 财经APP富途牛牛商业模式分析