前提・実現したいこと
Texture2Dをリサイズしたいのですが解像度? ピントが少しボケています。
良い方法があれば教えて頂けませんか。
scaner
resize (pcにて)
resize (スマホにて)
元の画像 (640,1136)
シェーダーのリサイズ
canvas/image
canvas
該当のソースコード
unity
1 private static Texture2D GetResized(Texture2D texture, int width, int height) 2 { 3 // リサイズ後のサイズを持つRenderTextureを作成して書き込む 4 var rt = RenderTexture.GetTemporary(width, height); 5 Graphics.Blit(texture, rt); 6 7 // リサイズ後のサイズを持つTexture2Dを作成してRenderTextureから書き込む 8 var preRT = RenderTexture.active; 9 RenderTexture.active = rt; 10 var ret = new Texture2D(width, height); 11 ret.ReadPixels(new Rect(0, 0, width, height), 0, 0); 12 ret.Apply(); 13 RenderTexture.active = preRT; 14 15 RenderTexture.ReleaseTemporary(rt); 16 return ret; 17 }
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/05/04 05:00 編集
2020/05/04 05:42
2020/05/04 05:51
2020/05/04 22:05 編集
2020/05/04 06:34
2020/05/04 07:00
2020/05/04 07:07
回答1件
0
ベストアンサー
確かにちょっとぼやけているように感じますね。
元の画像が
のとき(サイズ640×640)、ご質問者さんの方法で200×200に縮小すると
となるのに対して、Photoshopの「バイリニア法」だと
「バイキュービック法 - シャープ (縮小)」だと
といった結果になり、ぼやけの程度が小さく抑えられています。
毎フレーム行う処理でないのなら多少処理コストがかかってもかまわないでしょうから、自前でバイキュービック法のような縮小方法を実装するとか、あるいはアンシャープマスク処理を加えてやるのがいいかもしれません。
あるいは単純に、縮小前のテクスチャ中において縮小後の1ピクセルが占める領域を何カ所もサンプリングするというのはどうでしょうか。
たとえば下記のようなシェーダーを用意しておいて...
ShaderLab
1// 字数節約のため削除しました...削除前のコードは編集履歴をご参照ください
ご質問者さんのスクリプトを下記のように改変して実行したところ...
C#
1using System.IO; 2using UnityEditor; 3using UnityEngine; 4 5public static class TextureResizer 6{ 7 private static Material overSamplingMaterial; 8 9 [MenuItem("Utility/Resize Selected Texture to 200 x 200")] 10 public static void ResizeTextureTo200x200() 11 { 12 const int newWidth = 200; 13 const int newHeight = 200; 14 const int loopCount = 16; 15 var targetTexture = Selection.activeObject as Texture2D; 16 if (targetTexture == null) 17 { 18 return; 19 } 20 21 var targetPath = AssetDatabase.GetAssetPath(targetTexture); 22 if (string.IsNullOrEmpty(targetPath)) 23 { 24 return; 25 } 26 27 var targetDirectory = Path.GetDirectoryName(targetPath); 28 var targetName = Path.GetFileNameWithoutExtension(targetPath); 29 var targetExtension = Path.GetExtension(targetPath); 30 var newName = $"{targetName}({newWidth}x{newHeight})"; 31 var newPath = $"{targetDirectory}{Path.DirectorySeparatorChar}{newName}{targetExtension}"; 32 var newTexture = GetResized(targetTexture, newWidth, newHeight, loopCount); 33 File.WriteAllBytes(newPath, newTexture.EncodeToPNG()); 34 AssetDatabase.ImportAsset(newPath); 35 } 36 37 private static Texture2D GetResized(Texture2D texture, int width, int height, int loopCount) 38 { 39 // オーバーサンプリング用マテリアルを作成する 40 if (overSamplingMaterial == null) 41 { 42 overSamplingMaterial = new Material(Shader.Find("Hidden/OverSampling")); 43 } 44 45 // まず元と同じサイズのRenderTextureに書き込む 46 var sourceRT = RenderTexture.GetTemporary(texture.width, texture.height); 47 Graphics.Blit(texture, sourceRT); 48 49 // filterModeをPointにしておき、サンプリング時に注目点以外の色が混じらないようにしておく 50 sourceRT.filterMode = FilterMode.Point; 51 52 // リサイズ後のサイズを持つRenderTextureを作成して、オーバーサンプリングマテリアルを使って書き込む 53 var rt = RenderTexture.GetTemporary(width, height); 54 overSamplingMaterial.SetInt("_LoopCount", Mathf.Max(loopCount, 1)); 55 Graphics.Blit(sourceRT, rt, overSamplingMaterial); 56 57 // リサイズ後のサイズを持つTexture2Dを作成してRenderTextureから書き込む 58 var preRT = RenderTexture.active; 59 RenderTexture.active = rt; 60 var ret = new Texture2D(width, height); 61 ret.ReadPixels(new Rect(0, 0, width, height), 0, 0); 62 ret.Apply(); 63 RenderTexture.active = preRT; 64 RenderTexture.ReleaseTemporary(rt); 65 RenderTexture.ReleaseTemporary(sourceRT); 66 return ret; 67 } 68}
loopCount
が1だと(これは実質的にニアレストネイバー法と同等のはずです)...
loopCount
が4だと(1ピクセルにつき16点サンプリング)...
loopCount
が16だと(1ピクセルにつき256点サンプリング)...
となりました。だいぶ精密さが向上したんじゃないかと思いますが、いかがでしょうか?
正方形トリミングについて
まずシェーダーコードは下記のように変更して、UV座標を操作することで画像切り出し範囲を調整できるようにしておきます。
ShaderLab
1Shader "Hidden/OverSampling" 2{ 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 } 7 SubShader 8 { 9 Cull Off ZWrite Off ZTest Always 10 11 Pass 12 { 13 CGPROGRAM 14 #pragma vertex vert 15 #pragma fragment frag 16 17 #include "UnityCG.cginc" 18 19 struct appdata 20 { 21 float4 vertex : POSITION; 22 float2 uv : TEXCOORD0; 23 }; 24 25 struct v2f 26 { 27 float2 uv : TEXCOORD0; 28 float4 vertex : SV_POSITION; 29 }; 30 31 sampler2D _MainTex; 32 float4 _TilingAndOffset; 33 int _LoopCount; 34 35 v2f vert(appdata v) 36 { 37 v2f o; 38 o.vertex = UnityObjectToClipPos(v.vertex); 39 o.uv = (v.uv * _TilingAndOffset.xy) + _TilingAndOffset.zw; 40 return o; 41 } 42 43 float4 frag (v2f i) : SV_Target 44 { 45 float2 texelSize = fwidth(i.uv); 46 float2 deltaUV = texelSize / _LoopCount; 47 float2 uv0 = i.uv + ((deltaUV - texelSize) * 0.5); 48 float4 col = 0.0; 49 for (int j = 0; j < _LoopCount; j++) 50 { 51 for (int i = 0; i < _LoopCount; i++) 52 { 53 col += tex2D(_MainTex, uv0 + (float2(i, j) * deltaUV)); 54 } 55 } 56 return col / (_LoopCount * _LoopCount); 57 } 58 ENDCG 59 } 60 } 61}
そして制御用のスクリプトは下記のようにしました。
ご追記のスクリーンショットを拝見しますに、どうやらImageを使って縮小後のテクスチャを表示している様子ですね。表示用のスプライトはどのように用意していますでしょうか?私の場合はさしあたり縮小後のテクスチャを使ったスプライトを新規作成し、それをImage
コンポーネントにセットしてやることにしました。
スクリプトはサイズ200×200のImageオブジェクトにアタッチして使うことを想定しており、Start
内で元テクスチャをロード・縮小テクスチャを生成・スプライトを作成してImage
にセット...の処理を行っています。
C#
1using UnityEngine; 2using UnityEngine.UI; 3 4[RequireComponent(typeof(Image))] 5public class ResizedImageLoader : MonoBehaviour 6{ 7 private static Material overSamplingMaterial; 8 9 [SerializeField] private bool useActualSize; 10 11 private void Start() 12 { 13 var texture2D = Resources.Load<Texture2D>("motogp"); 14 15 // 目標サイズは200x200決め打ちではなく、Imageのサイズを見て決めることにする 16 var image = this.GetComponent<Image>(); 17 var rectTransform = this.transform as RectTransform; 18 var sizeDelta = rectTransform.sizeDelta; 19 var toWidth = Mathf.RoundToInt(sizeDelta.x); 20 var toHeight = Mathf.RoundToInt(sizeDelta.y); 21 22 // インスペクターの「Use Actual Size」がオンの場合 23 // 目標サイズを最終的な表示サイズに合わせて調整する 24 if (this.useActualSize) 25 { 26 var canvas = image.canvas; 27 var canvasCamera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; 28 var corners = new Vector3[4]; 29 rectTransform.GetWorldCorners(corners); 30 var bottomLeft = RectTransformUtility.WorldToScreenPoint(canvasCamera, corners[0]); 31 var topRight = RectTransformUtility.WorldToScreenPoint(canvasCamera, corners[2]); 32 var actualSize = topRight - bottomLeft; 33 toWidth = Mathf.RoundToInt(Mathf.Abs(actualSize.x)); 34 toHeight = Mathf.RoundToInt(Mathf.Abs(actualSize.y)); 35 } 36 37 // 元のテクスチャから切り出すべき領域を求める 38 // テクスチャの中心からなるべく大きな矩形を切り出したいので 39 // まずテクスチャの高さに縦横比を掛けて希望切り出し幅を求める 40 // テクスチャの幅が希望切り出し幅よりも小さい場合、切り出し幅は 41 // テクスチャ幅とし、代わりに切り出し高さを切り詰める 42 // 起点のズレは余白の大きさの半分ということになる 43 var toAspect = (float)toWidth / toHeight; 44 var width = texture2D.width; 45 var height = texture2D.height; 46 var expectedWidth = Mathf.RoundToInt(height * toAspect); 47 var fromWidth = Mathf.Min(width, expectedWidth); 48 var fromHeight = expectedWidth > width ? Mathf.RoundToInt(width / toAspect) : height; 49 var offsetX = (width - fromWidth) / 2; 50 var offsetY = (height - fromHeight) / 2; 51 52 // 確認のため、リサイズ前とリサイズ後のサイズをコンソールに表示してみる 53 Debug.LogFormat("({0}, {1}) -> [({2}, {3}), ({4}, {5})] -> ({6}, {7})", 54 width, height, 55 offsetX, offsetY, fromWidth, fromHeight, 56 toWidth, toHeight); 57 58 // 切り出された領域をリサイズして最終的なテクスチャとする 59 // ちなみに、ご質問者さんが作成された方のGetResizedも残してありますので、 60 // 引数を元テクスチャと目標サイズだけに省略するとそちらが使われます 61 // そちらに切り替えてみると、トリミング処理の有無による違いやぼけ具合の 62 // 比較ができるかと思います 63 var resizedTexture = GetResized(texture2D, offsetX, offsetY, fromWidth, fromHeight, toWidth, toHeight); 64 //var resizedTexture = GetResized(texture2D, 200, 200); 65 66 // 最終的に表示される位置・サイズによっては、小数点以下のズレのため 67 // ぼやけやImage外周部に反対側の辺の色が乗る現象が起こる可能性がある 68 // そこで、それらを回避するようにテクスチャの設定を変更する 69 resizedTexture.filterMode = FilterMode.Point; 70 resizedTexture.wrapMode = TextureWrapMode.Clamp; 71 72 // リサイズ後のテクスチャをもとにスプライトを作成し、Imageにセット 73 var sprite = Sprite.Create( 74 resizedTexture, 75 new Rect(0, 0, resizedTexture.width, resizedTexture.height), 76 new Vector2(0.5f, 0.5f)); 77 image.sprite = sprite; 78 } 79 80 // 元のテクスチャから(fromX, fromY)を起点とする幅fromWidth、高さfromHeightの領域を 81 // 切り出し、それを幅toWidth、高さtoHeightにリサイズしたテクスチャを生成する 82 private static Texture2D GetResized( 83 Texture2D texture, 84 int fromX, int fromY, int fromWidth, int fromHeight, 85 int toWidth, int toHeight, 86 int loopCount = 16, 87 bool generateMipmaps = false) 88 { 89 if (overSamplingMaterial == null) 90 { 91 overSamplingMaterial = new Material(Shader.Find("Hidden/OverSampling")); 92 } 93 94 // まずは以前同様に元テクスチャをRenderTextureに写し取り、FilterModeをPointにしておく 95 var preRT = RenderTexture.active; 96 var sourceWidth = texture.width; 97 var sourceHeight = texture.height; 98 var sourceRT = RenderTexture.GetTemporary(sourceWidth, sourceHeight); 99 Graphics.Blit(texture, sourceRT); 100 sourceRT.filterMode = FilterMode.Point; 101 102 // マテリアルの設定を行う 103 // 元のテクスチャから起点(fromX, fromY)、サイズ(fromWidth, fromHeight)の部分を 104 // 切り出すということは、UV座標のタイリング・オフセットは下記のようになる 105 overSamplingMaterial.SetVector( 106 "_TilingAndOffset", 107 new Vector4( 108 (float)fromWidth / sourceWidth, 109 (float)fromHeight / sourceHeight, 110 (float)fromX / sourceWidth, 111 (float)fromY / sourceHeight)); 112 overSamplingMaterial.SetInt("_LoopCount", Mathf.Max(loopCount, 1)); 113 114 // あとは以前と同様にリサイズし、結果を返す 115 var rt = RenderTexture.GetTemporary(toWidth, toHeight); 116 Graphics.Blit(sourceRT, rt, overSamplingMaterial); 117 RenderTexture.active = rt; 118 var ret = new Texture2D(toWidth, toHeight, TextureFormat.ARGB32, generateMipmaps); 119 ret.ReadPixels(new Rect(0, 0, toWidth, toHeight), 0, 0); 120 ret.Apply(); 121 RenderTexture.active = preRT; 122 RenderTexture.ReleaseTemporary(rt); 123 RenderTexture.ReleaseTemporary(sourceRT); 124 return ret; 125 } 126 127 // ご質問者さんの方法によるGetResized 128 private static Texture2D GetResized(Texture2D texture, int width, int height) 129 { 130 var rt = RenderTexture.GetTemporary(width, height); 131 Graphics.Blit(texture, rt); 132 var preRT = RenderTexture.active; 133 RenderTexture.active = rt; 134 var ret = new Texture2D(width, height); 135 ret.ReadPixels(new Rect(0, 0, width, height), 0, 0); 136 ret.Apply(); 137 RenderTexture.active = preRT; 138 RenderTexture.ReleaseTemporary(rt); 139 return ret; 140 } 141}
当初の方法だと、縦長の画像が正方形に縮小されることで縦に潰れてしまっていたのが...
正方形に切り出す処理が加わることで、被写体が潰れないようになりました。
投稿2020/05/06 06:33
編集2020/05/13 08:30総合スコア10811
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/05/09 11:08 編集
2020/05/09 11:22
2020/05/09 22:06
2020/05/09 22:49 編集
2020/05/09 23:08 編集
2020/05/09 23:29
2020/05/09 23:46
2020/05/10 00:24 編集
2020/05/10 01:34 編集
2020/05/10 02:47 編集
2020/05/10 09:35
2020/05/11 10:44 編集
2020/05/11 11:36
2020/05/13 19:04 編集
2020/05/13 08:31
2020/05/13 09:06
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。