当前位置:   article > 正文

Unity图片优化 - Dither算法

dither算法

///

 在Unity中常用的的图片格式有RGBA32,RGBA16,ETC,PVRTC等。这里我们主要讨论带透明通道的RGBA32和RGBA16这两种格式的图片在Unity占用的内存空间已经优化方案。

我们知道,RGBA32是R,G,B,A四个通道每个通道用8位来表示,RGBA16则是用4位。所以RGBA32能够带来更好的显示效果。同时也会带来更大的内存消耗。下面给两张RGBA16和RGBA32的对比图。

RGBA32

 RGBA32.png

RGBA16

 RGBA16.png

从上面两个图可以看得出,RGBA32能够带来更好的显示效果。而RGBA16在有些地方的色阶太明显,导致显示效果不尽人意。由此keijiro(Github地址)写了一个dither算法来消除这种色阶,以达到高于RGBA16低于RGBA32的显示效果。

下图为Dither优化之后的RGBA16

 RGBA16-Dither.png

通过对比能明显看出优化后的RGBA16能够消除色阶,如果不是放大特意看,和RGBA32几乎差别不同。通过这种方式将内存降低一半确认带来更好的效果。

万事总会存在利弊,这个算法在消除了色阶的同事,带来的是更多的噪点。所以这个方法不适用于图片需要放大来显示的。总体来说,该方案在一定程度上还是能够带来很好的效果。
最后把算法的核心代码贴出来 。

  1. public class TextureImportSetting : AssetPostprocessor {
  2. string[] assetTexturePath = new string[]{"Assets/Resources/TextureVN/"}; //放置需要优化的路径
  3. void OnPreprocessTexture(){
  4. foreach (var str in assetTexturePath)
  5. {
  6. if (this.assetPath.StartsWith(str) )
  7. {
  8. TextureImporter textureImporter = this.assetImporter as TextureImporter;
  9. textureImporter.textureType = TextureImporterType.Advanced;
  10. textureImporter.npotScale = TextureImporterNPOTScale.ToNearest;
  11. textureImporter.mipmapEnabled = false;
  12. textureImporter.isReadable = false;
  13. if ( textureImporter.DoesSourceTextureHaveAlpha())
  14. {
  15. textureImporter.textureFormat = TextureImporterFormat.RGBA32;
  16. }
  17. }
  18. }
  19. }
  20. public static void OnPostprocessRGB16 (Texture2D texture)
  21. {
  22. var texw = texture.width;
  23. var texh = texture.height;
  24. var pixels = texture.GetPixels ();
  25. var offs = 0;
  26. var k1Per15 = 1.0f / 15.0f;
  27. var k1Per16 = 1.0f / 16.0f;
  28. var k3Per16 = 3.0f / 16.0f;
  29. var k5Per16 = 5.0f / 16.0f;
  30. var k7Per16 = 7.0f / 16.0f;
  31. for (var y = 0; y < texh; y++) {
  32. for (var x = 0; x < texw; x++) {
  33. float a = pixels [offs].a;
  34. float r = pixels [offs].r;
  35. float g = pixels [offs].g;
  36. float b = pixels [offs].b;
  37. var a2 = Mathf.Clamp01 (Mathf.Floor (a * 16) * k1Per15);
  38. var r2 = Mathf.Clamp01 (Mathf.Floor (r * 16) * k1Per15);
  39. var g2 = Mathf.Clamp01 (Mathf.Floor (g * 16) * k1Per15);
  40. var b2 = Mathf.Clamp01 (Mathf.Floor (b * 16) * k1Per15);
  41. var ae = a - a2;
  42. var re = r - r2;
  43. var ge = g - g2;
  44. var be = b - b2;
  45. pixels [offs].a = a2;
  46. pixels [offs].r = r2;
  47. pixels [offs].g = g2;
  48. pixels [offs].b = b2;
  49. var n1 = offs + 1;
  50. var n2 = offs + texw - 1;
  51. var n3 = offs + texw;
  52. var n4 = offs + texw + 1;
  53. if (x < texw - 1) {
  54. pixels [n1].a += ae * k7Per16;
  55. pixels [n1].r += re * k7Per16;
  56. pixels [n1].g += ge * k7Per16;
  57. pixels [n1].b += be * k7Per16;
  58. }
  59. if (y < texh - 1) {
  60. pixels [n3].a += ae * k5Per16;
  61. pixels [n3].r += re * k5Per16;
  62. pixels [n3].g += ge * k5Per16;
  63. pixels [n3].b += be * k5Per16;
  64. if (x > 0) {
  65. pixels [n2].a += ae * k3Per16;
  66. pixels [n2].r += re * k3Per16;
  67. pixels [n2].g += ge * k3Per16;
  68. pixels [n2].b += be * k3Per16;
  69. }
  70. if (x < texw - 1) {
  71. pixels [n4].a += ae * k1Per16;
  72. pixels [n4].r += re * k1Per16;
  73. pixels [n4].g += ge * k1Per16;
  74. pixels [n4].b += be * k1Per16;
  75. }
  76. }
  77. offs++;
  78. }
  79. }
  80. texture.SetPixels (pixels);
  81. EditorUtility.CompressTexture (texture, TextureFormat.RGBA4444, TextureCompressionQuality.Best);
  82. }
  83. void OnPostprocessTexture(Texture2D texture){
  84. foreach (var str in assetTexturePath)
  85. {
  86. if (this.assetPath.StartsWith(str) )
  87. {
  88. TextureImporter textureImporter = this.assetImporter as TextureImporter;
  89. if ( textureImporter.DoesSourceTextureHaveAlpha())
  90. {
  91. OnPostprocessRGB16(texture);
  92. }
  93. }
  94. }
  95. }
  96. }

//

在Unity移动平台的游戏开发过程中,贴图资源是往往是占资源量最大的资源。如何在保证视觉效果的同时,尽可能地减少贴图资源,是开发团队会经常遇到的问题。通常来说,对于3D物体的纹理,是可以采用ETC/PVRTC等压缩比很大的算法处理,但是对于细节要求很高的UI纹理,这样处理造成的失真往往达不到质量要求。对于这类的贴图,我们可以考虑使用失真较小的16位贴图格式存储。

但是对于颜色数较高的纹理,Unity提供的默认转换方法会呈现明显的色阶。针对该问题,keijiro实现了一种Dither4444的改进算法。从下图1上可以看到,对于画面细节比较平滑的图片,该算法虽然消除了色阶现象,同时带来了肉眼可见的噪点。

请输入图片描述


<图1> 左:原始图 右:keijiro的 Dither4444示意图

笔者在keijiro的算法基础上进行了改进,提供了一个将RGB24 Bit图Dither之后转RGB565的方法,基本消除了肉眼可见的失真,实际效果见下图2。

请输入图片描述


<图2> 左:原始图 右:笔者的 Dither565示意图

实际在我的项目的应用中,对于不适合ETC/PVRTC压缩的图片,都采用了该文章中的RGB565或者RGB565+A8的方式。在肉眼基本无失真的基础上,节省了部分资源。

最后附上OnPostprocessTexture代码:

  1. void OnPostprocessTexture (Texture2D texture)
  2. {
  3. if(assetPath.Contains ("_dither565"))
  4. {
  5. var texw = texture.width;
  6. var texh = texture.height;
  7. var pixels = texture.GetPixels ();
  8. var offs = 0;
  9. var k1Per31 = 1.0f / 31.0f;
  10. var k1Per32 = 1.0f / 32.0f;
  11. var k5Per32 = 5.0f / 32.0f;
  12. var k11Per32 = 11.0f / 32.0f;
  13. var k15Per32 = 15.0f / 32.0f;
  14. var k1Per63 = 1.0f / 63.0f;
  15. var k3Per64 = 3.0f / 64.0f;
  16. var k11Per64 = 11.0f / 64.0f;
  17. var k21Per64 = 21.0f / 64.0f;
  18. var k29Per64 = 29.0f / 64.0f;
  19. var k_r = 32; //R&B压缩到5位,所以取25次方
  20. var k_g = 64; //G压缩到6位,所以取26次方
  21. for(var y = 0; y < texh; y++){
  22. for(var x = 0; x < texw; x++){
  23. float r = pixels [offs].r;
  24. float g = pixels [offs].g;
  25. float b = pixels [offs].b;
  26. var r2 = Mathf.Clamp01 (Mathf.Floor (r * k_r) * k1Per31);
  27. var g2 = Mathf.Clamp01 (Mathf.Floor (g * k_g) * k1Per63);
  28. var b2 = Mathf.Clamp01 (Mathf.Floor (b * k_r) * k1Per31);
  29. var re = r - r2;
  30. var ge = g - g2;
  31. var be = b - b2;
  32. var n1 = offs + 1;
  33. var n2 = offs + texw - 1;
  34. var n3 = offs + texw;
  35. var n4 = offs + texw + 1;
  36. if(x < texw - 1){
  37. pixels [n1].r += re * k15Per32;
  38. pixels [n1].g += ge * k29Per64;
  39. pixels [n1].b += be * k15Per32;
  40. }
  41. if(y < texh - 1){
  42. pixels [n3].r += re * k11Per32;
  43. pixels [n3].g += ge * k21Per64;
  44. pixels [n3].b += be * k11Per32;
  45. if(x > 0){
  46. pixels [n2].r += re * k5Per32;
  47. pixels [n2].g += ge * k11Per64;
  48. pixels [n2].b += be * k5Per32;
  49. }
  50. if(x < texw - 1){
  51. pixels [n4].r += re * k1Per32;
  52. pixels [n4].g += ge * k3Per64;
  53. pixels [n4].b += be * k1Per32;
  54. }
  55. }
  56. pixels [offs].r = r2;
  57. pixels [offs].g = g2;
  58. pixels [offs].b = b2;
  59. offs++;
  60. }
  61. }
  62. texture.SetPixels (pixels);
  63. EditorUtility.CompressTexture (texture, TextureFormat.RGB565, TextureCompressionQuality.Best);
  64. }
  65. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/636883
推荐阅读
相关标签
  

闽ICP备14008679号