17
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UnityAdvent Calendar 2023

Day 6

ランタイムでTexture2DをASTCテクスチャに圧縮しロードする

Last updated at Posted at 2023-12-05

前書き

この記事は、2023のUnityアドカレの12/6の記事です。
今年は、完走賞に挑戦してみたいと思います。Qiita君ぬい欲しい!

TL;DR;

// https://github.com/niepp/astc_encoder
GraphicsBuffer ConvertAstc(Texture2D raw);

Texture2D screenShot = ...;
using var astcBuff = ConvertAstc(screenShot);

var texture = new Texture2D(width, height, TextureFormat.ASTC_6x6);
using var map = texture.GetRawTextureData<byte>();
astcBuff.GetData(map);
texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);

圧縮テクスチャ

一般的に画像の圧縮といいますと、JPEGやPNGが主流です。しかし、これらのフォーマットはGPUで解釈することができず、CPUでデコードし、GPUに送信するという手順を経て表示されます。

一方で、テクスチャ向け圧縮は、GPUがネイティブで解釈ができるように設計されています。PNGなどとの決定的な違いは、画像をブロック状に分割し、各ブロックのサイズが同じになっています。故に、読み出したいピクセルがあったときに、画像全体をデコードせずとも、ピクセルのデータがどのあたりにあるのかをアドレス計算で求めることができます。

主なテクスチャ向け圧縮は、ETC、DTX、ASTCなどがあります。昨今では、ASTCが圧縮率を高めても品質に優れているということで採用することが多くなっている印象です。iOS/Android/PCの大体で使えるようになってきたことも大きいです。

Texture2D.Compress

Texture2Dをランタイムで圧縮したいときと言えば、Texture2D.Compressです。

しかし、これは、PCならDXT1/DXT5、モバイルならETC2に固定されてしまっています。

Texture2D screenShot = ...;
Debug.Log(screenShot.format); // RGBA32

screenShot.Compress(quality);
screenShot.Apply();
Debug.Log(screenShot.format); // ETC2

しかもこれはCPUで計算をするので、そこそこ重たい計算です。参考までに、私の作業PCの一つでは50msぐらいかかりました。しかもUnityAPIなのでスレッドセーフではありません。フレーム落ち不可避。

ComputeShaderで圧縮する

ComputeShaderはGPUでグラフィック以外の処理をさせることができる(GPGPU)機能です。Vulkan、Metalでは必ずサポートされており、GLESも3.1でサポートされています。このComputeShaderでGPUパワーを使うことで、一瞬で圧縮ができます。

今回はこのShaderをUnityとグルーしてみます。

ASTC_Encode.hlslにカーネルが書いてあるので、これをUnityが読み込めるようにラップします。

ASTC_Encode.compute
#define BLOCK_6X6
#define HAS_ALPHA
#pragma kernel MainCS
#inclide "astc_encoder/ASTC_Encode.hlsl"

コマンドを組み実行します。astc_encode.hで書かれているDirectX実装を移植します。

Texture2D tex = ...; // 元画像

int DimSize = 6; // or 4
int TexWidth = tex.Width;
int TexHeight = tex.Height;
int xBlockNum = (TexWidth + DimSize - 1) / DimSize;
int yBlockNum = (TexHeight + DimSize - 1) / DimSize;
int TotalBlockNum = xBlockNum * yBlockNum;

int GroupSize = THREAD_NUM_X * THREAD_NUM_Y;
int GroupNum = (TotalBlockNum + GroupSize - 1) / GroupSize;
int GroupNumX = (TexWidth + DimSize - 1) / DimSize;
int GroupNumY = (GroupNum + GroupNumX - 1) / GroupNumX;

using var constData = new GraphicsBuffer(GraphicsBufferType.Constant, 1, sizeof(int) * 4);
using var outBuffer = new GraphicsBuffer(GraphicsBufferType.Structured, TotalBlockNum, 16), 

var cs = Resources.Load<ComputeShader>("ASTC_Encode");
constData.SetData(new [1]{ (TexWidth, TexHeight, GroupNumX, 0) });
cs.SetConstantBuffer(0, "constData", constData);
cs.SetTexture(0, "InTexture", tex);
cs.SetBuffer(0, "OutBuffer", outBuffer);
cs.Dispatch(0, GroupNumX, GroupNumY, 1);

var astc = new byte[TotalBlockNum * 16];
outBuffer.GetData<byte>(astc);

astc配列にASTC圧縮されたテクスチャデータが入っています。

Texture2Dにロードする

LoadRawTextureData を使うことで、ピクセル単位ではなく生データとしてロードできます。

var astcTex = new Texture2D(tex.Width, tex.Height, TextureFormat.ASTC_6x6);
astcTex.LoadRawTextureData(astc)
astcTex.Apply(updateMipmaps: false, makeNoLongerReadable: true);

Texture2Dをランタイムでnewすると、VRAMだけでなくメインRAMの方にもCPU用コピーが確保されてしまいますが、makeNoLongerReadable:falseでApplyすることで、これを開放することができます。

ASTCデータはただのバイト列ですので、ファイルにキャッシュしたり、サーバーに置くなり好きにすることができます。

ライセンス表記

niepp/astc_encoder
MIT License

Copyright (c) 2021 niedap

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
17
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?