最近翻出了最初学习Unity的项目,似乎我当时在研究像素屏幕,但表现的效果很有限。现将其重新实现,并添加新的功能。
在开始之前,我先给出本文章的实现效果:
屏幕视差深度、凸面屏幕
像素级缩放
自定义分辨率像素采样 图片大小限制,使用较低的GIF质量
RGB像素形状、LCD最低背光亮度、像素偏移
亮度、形状 | 像素偏移 |
---|---|
![]() |
![]() |
荧幕自发光亮度调整
用户交互
交互 | 细节 |
---|---|
![]() |
![]() |
目录
像素化采样
首先实现像素化采样,这是让屏幕从近距离看起来像液晶屏幕的基础。
我们需要提供两个参数:width
/height
分别表示横纵分辨率。
考虑一种极端情况,假设预期分辨率为2x2
,也就是只有四个像素:
方块内的每一个片段
(片段着色器所渲染的单位),都应该显示本方块采样点的颜色(图中的红点)。
则每一个片段
都需要知道自己的方块位于哪一个位置,进而根据方块位置计算采样点位置。如下图所示:
根据图中的逻辑,在连连看中实现采样点UV
的计算方法
之后根据这个坐标,即可直接进行图像采样:
采样 | 输出 |
---|---|
![]() |
![]() |
单像素边缘裁切
仔细观察输出结果,发现在方块边缘处会出现错误的颜色
首先需要明确,像素的显示时机是在显示三原色之前,因此每个像素之间需要存在一定间隙,这也是现实生活中的现象:
因此我们不会去解决边缘错色的问题,取而代之的是直接裁切每个像素的边缘,使其相互间隔一段距离,而不是连接在一起。
首先我们计算每个像素方块到边缘处的距离,即边缘的SDF:
其算法是:
首先使用减法和绝对值组合,使范围从(0,1)
映射到(0.5,0)(0,0.5)
之后一个减法将范围从(0.5,0)(0,0.5)
映射到(0,0.5)(0.5,0)
,使其恰好反映出到边缘的距离。
之后使用step
函数,让最接近边缘的片段输出为1
,其他部分输出为0
。
为了绘制出最细的线段,使用DDXY
来实现标记最靠近边缘的片段
。
如果你对
DDXY
在这里的用法感到疑惑,在上一篇文章中,我解释了使用DDXY绘制单像素抗锯齿网格的原理_CNDS。通俗的讲,DDXY
在这里的作用是获取每一个片段
所占据的UV宽度。由此来裁切掉最靠近边缘的片段
,同时确保了结果不会失真。
你可以按照上一篇文章的内容,将step
换为smooth
,以消除边缘锯齿,代价是牺牲部分性能。
将结果的01值,对像素颜色使用乘法可以做到裁切像素的效果
动态裁切像素边缘
本节开始出现容易混淆的名词,在此先强调:
”屏幕像素“
指的是真实的像素,是实体显示器上显示出来的像素。
“仿真像素”
指的是我们模拟出来的虚假的仿真像素,是由多个屏幕像素
组合而成。
”片段“
指的是在Shader中,片段着色器的基本单位
没有开启多重抗锯齿时,片段
和屏幕像素
是大小相等,一一对应的,本文建立在此基础之上。
现在存在一个问题:如果仿真像素
分辨率极大,或摄像机距离较远,会导致大部分片段
被当作仿真像素
的边缘而裁切(因为最靠近边缘的片段
始终被裁切,而整个仿真像素
的宽度可能才只有3个片段
)。
为了防止这类情况,需要在恰当时机显示边缘,而不是总是裁切。
我们希望当画面上的仿真像素
比屏幕像素
更大时显示边缘,反之则裁切。
为了做到这一点,我们需要将仿真像素
的UV宽度,与屏幕像素
的UV宽度比较,并以此为权重显示对应的内容。
注意
:不要试图使用摄像机位置作为影响因子来管理这一过程。因为摄像机位置无法反应出焦距、UV视差深度的变化。
注意
:DDXY
计算的结果是相较于前一个片段
的差值,将其乘以二用于大致估算此片段的完整UV宽度。一个并不准确的示意图(此图仅为示意):