| 
 | 
 
 
 楼主 |
发表于 2023-3-1 19:38:13
|
显示全部楼层
 
 
 
 本帖最后由 dasasdhba 于 2023-6-27 14:39 编辑  
 
[25] 图像放大算法 
下面我们介绍游戏常用的放大算法 
再重复一遍,Fragment Shader 不能改变 Texture 本身尺寸,为了得到放大后的结果,一般采取与 Vertex Shader 协助的办法(参考 [9]),因为无论是人为留空还是提前临近处理都会带来不必要的麻烦。 
 
1. HQnx 
2003 年就被提出的比较老的算法,实现的不算很漂亮,甚至找不到什么原理解析,下面给出我的个人理解。 
HQnx 用于像素风格图像的放大,其中横线和竖线的放大是容易的,关键在于对斜线放大的处理。 
 
(1). 检测斜线 
我们简单地考虑每个像素周围 3*3 的区域,不考虑镜像,斜线可分为两类: 
 
a. 双边线 
■□□ 
□■□ 
□□■ 
b. 单边线 
■■■ 
□■■ 
□□■ 
 
若考虑镜像则有 6 类。 
 
为了检测斜线,我们使用特定的 3*3 卷积核对灰度(参考 [23])作卷积,这需要对上述黑方格■部分的灰度求均值并与原像素灰度比较,在差距不超过预设的某个阈值时就将其判定为某种类型的斜线。 
 
(2). 使用模板 
对于每种类型的斜线,我们将其处理为预先准备好的放大模板。如上述 a 情形可以使用模板: 
■■▲□□ 
■■■▲□ 
▲■■■▲ 
□▲■■■ 
□□▲■■ 
其中▲表示半透明混合(参考 [8]) 
 
然而,在具体实现上,主流的做法是采用 YUV 的方法获取权重,并利用 LUT 查表得到最后的 filter 值,可参考:https://github.com/CrossVR/hqx-shader/blob/master/glsl/hq2x.glsl 
 
2. xBRZ 
2011 年被提出的算法,基本思路是首先作边缘检测,然后判定每一个角落的像素是属于直角还是圆角,并基于这一点采取不同的插值达到较为合理的效果。 
 
因此具体如何边缘检测和判定角落类型会比较复杂,好在这个算法比较热门,已经有比较好的具体解析可供参考:https://www.luogu.com.cn/blog/ud2/xbrz-interpolation-explained 
 
3. Super SAI 
众所周知的 MF 的 Texture 采用的放大算法,于 1999 年即被提出,这个东西其实挺冷门的,但考虑到圈内应用广泛,我将其从 C# 版本(来自 imageresizer,这玩意其实是开源的)转写为了 GLSL 版本(可在 The book of Shader 网站上测试),并附带了相关注释,可供参考(但其实我也看不懂): 
- #ifdef GL_ES
 
 - precision mediump float;
 
 - #endif
 
  
- uniform sampler2D u_tex0;
 
 - uniform vec2 u_tex0Resolution;
 
  
- uniform vec2 u_resolution;
 
  
- // 用于 rgb 转 yuv
 
 - const mat3 yuv_matrix = mat3(
 
 -     0.299, -0.169, 0.5,
 
 -     0.587, -0.331, -0.419,
 
 -     0.114, 0.5,    -0.081);
 
 - const vec3 yuv_threshold = vec3(48.0/255.0, 7.0/255.0, 6.0/255.0);
 
 - const vec3 yuv_offset = vec3(0, 0.5, 0.5);
 
  
- // 通过 yuv 判断两个颜色的相似度
 
 - bool like(vec4 c1, vec4 c2) {
 
 -     vec3 a = yuv_matrix * c1.rgb;
 
 -     vec3 b = yuv_matrix * c2.rgb;
 
  
-     bvec3 res = greaterThan(abs((a + yuv_offset) - (b + yuv_offset)), yuv_threshold);
 
 -         return !(res.x || res.y || res.z);
 
 - }
 
  
- // 一个莫名其妙的函数
 
 - // a 与 c d 相似返回 -1
 
 - // a 不与 c d 相似且 b 与 c d 相似返回 1
 
 - // 其他情况返回 0
 
 - float cond(vec4 a, vec4 b, vec4 c, vec4 d) {
 
 -     bool ac = like(a, c);
 
 -     bool ad = like(a, d);
 
 -     bool bc = like(b, c);
 
 -     bool bd = like(b, d);
 
  
-     float x = float(ac) + float(ad);
 
 -     float y = float(bc && !ac) + float(bd && !ad);
 
 -     return float(x <= 1.0) - float(y <= 1.0);
 
 - }
 
  
- void main () {
 
 -     vec2 uv = gl_FragCoord.xy/u_resolution.xy;
 
 -     vec2 unit = 1.0/u_tex0Resolution;
 
  
-     // c0  c1  c2  d3
 
 -     // c3  c4  c5  d4
 
 -     // c6  c7  c8  d5
 
 -     // d0  d1  d2  d6
 
 -     // 其中 c4 在当前 UV 位置
 
 -     vec4 c0 = texture2D(u_tex0, uv + vec2(-1.0, -1.0)*unit);
 
 -     vec4 c1 = texture2D(u_tex0, uv + vec2( 0.0, -1.0)*unit);
 
 -     vec4 c2 = texture2D(u_tex0, uv + vec2( 1.0, -1.0)*unit);
 
 -     vec4 d3 = texture2D(u_tex0, uv + vec2( 2.0, -1.0)*unit);
 
 -     vec4 c3 = texture2D(u_tex0, uv + vec2(-1.0,  0.0)*unit);
 
 -     vec4 c4 = texture2D(u_tex0, uv + vec2( 0.0,  0.0)*unit);
 
 -     vec4 c5 = texture2D(u_tex0, uv + vec2( 1.0,  0.0)*unit);
 
 -     vec4 d4 = texture2D(u_tex0, uv + vec2( 2.0,  0.0)*unit);
 
 -     vec4 c6 = texture2D(u_tex0, uv + vec2(-1.0,  1.0)*unit);
 
 -     vec4 c7 = texture2D(u_tex0, uv + vec2( 0.0,  1.0)*unit);
 
 -     vec4 c8 = texture2D(u_tex0, uv + vec2( 1.0,  1.0)*unit);
 
 -     vec4 d5 = texture2D(u_tex0, uv + vec2( 2.0,  1.0)*unit);
 
 -     vec4 d0 = texture2D(u_tex0, uv + vec2(-1.0,  2.0)*unit);
 
 -     vec4 d1 = texture2D(u_tex0, uv + vec2( 0.0,  2.0)*unit);
 
 -     vec4 d2 = texture2D(u_tex0, uv + vec2( 1.0,  2.0)*unit);
 
 -     vec4 d6 = texture2D(u_tex0, uv + vec2( 2.0,  2.0)*unit);
 
  
-     // e00 e01
 
 -     // e10 e11
 
 -     // 放大后输出的四个像素的颜色
 
 -     vec4 e00 = c4;
 
 -     vec4 e01 = c4;
 
 -     vec4 e10;
 
 -     vec4 e11 = c4;
 
  
-     // 处理 e01 与 e11
 
 -     if (like(c7, c5) && !like(c4, c8)) {
 
 -         vec4 c57 = mix(c7, c5, 0.5);
 
 -         e11 = c57;
 
 -         e01 = c57;
 
 -     } else if (like(c4, c8) && !like(c7, c5)) {
 
 -         // pass
 
 -     } else if (like(c4, c8) && like(c7, c5)) {
 
 -         vec4 c57 = mix(c7, c5, 0.5);
 
 -         vec4 c48 = mix(c4, c8, 0.5);
 
  
-         // 谁能告诉我它在干嘛???
 
 -         float conc = 0.0;
 
 -         conc += cond(c57, c48, c6, d1);
 
 -         conc += cond(c57, c48, c3, c1);
 
 -         conc += cond(c57, c48, d2, d5);
 
 -         conc += cond(c57, c48, c2, d4);
 
  
-         if (conc > 0.0) {
 
 -             e11 = c57;
 
 -             e01 = c57;
 
 -         } else if (conc == 0.0) {
 
 -             e11 = mix(c48, c57, 0.5);
 
 -             e01 = e11;
 
 -         }
 
 -     } else {
 
 -         // 地狱绘图.jpg
 
 -         if (like(c8, c5) && like(c8, d1) && !like(c7, d2) && !like(c8, d0)) {
 
 -             e11 = mix((c8+c5+d1)/3.0, c7, 0.75);
 
 -         } else if (like(c7, c4) && like(c7, d2) && !like(c7,d6), !like(c8, d1)) {
 
 -             e11 = mix((c7+c4+d2)/3.0, c8, 0.75);
 
 -         } else {
 
 -             e11 = mix(c7, c8, 0.5);
 
 -         }
 
  
-         if (like(c5, c8) && like(c5, c1) && !like(c5, c0) && !like(c4, c2)) {
 
 -             e01 = mix((c5+c8+c1)/3.0, c4, 0.75);
 
 -         } else if (like(c4, c7) && like(c4, c2) && !like(c5, c1) && !like(c4, d3)) {
 
 -             e01 = mix((c4+c7+c2)/3.0, c5, 0.75);
 
 -         } else {
 
 -             e01 = mix(c4, c5, 0.5);
 
 -         }
 
 -     }
 
  
-     // 处理 e10
 
 -     if (like(c4, c8) && like(c4, c3) && !like(c7, c5) && !like(c4, d2)) {
 
 -         e10 = mix(c7, (c4+c8+c3)/3.0, 0.5);
 
 -     } else if (like(c4, c6) && like(c4, c5) && !like(c7, c3) && !like(c4 ,d0)) {
 
 -         e10 = mix(c7, (c4+c6+c5)/3.0, 0.5);
 
 -     } else {
 
 -         e10 = c7;
 
 -     }
 
  
-     // 处理 e00
 
 -     if (like(c7, c5) && like(c7, c6) && !like(c4, c8) && !like(c7, c2)) {
 
 -         e00 = mix((c7+c5+c6)/3.0, c4, 0.5);
 
 -     } else if (like(c7, c3) && like(c7, c8) && !like(c4, c6) && !like(c7, c0)) {
 
 -         e00 = mix((c7+c3+c8)/3.0, c4, 0.5);
 
 -     }
 
  
-     // 混和结果
 
 -     vec2 pos = floor(fract(uv * u_tex0Resolution) * 2.0);
 
 -     vec4 color = mix(
 
 -                 mix(e00, e01, pos.x),
 
 -                 mix(e10, e11, pos.x),
 
 -                 pos.y);
 
  
-     gl_FragColor = color;
 
 - }
 
 
  复制代码 
注:此为源代码转写,没有优化性能,比如 YUV 转换这里很明显多算了很多次 |   
 
 
 
 |