3D texture
サイバーエージェントさんのUnity用エフェクトシェーダについての記事を読んで、Unity上で3Dテクスチャが簡単に作成できるようになっているのを知りました。
これはスプライトアニメーションに使えそうです。さっそく実装してみました。
左が9パターンにアニメーション(ブレンドあり)、中央がモデルアニメーション、右が通常のスプライトアニメーションです。
アニメーションパターンが多い場合はそれほど変わり映えしませんし、少ない場合でも変にブレると気持ち悪いということもあると思いますが、使い方によっては良い効果が得られるのではないかと思います。
作成方法-Flipbook Animation
まずはアニメーションパターンのテクスチャを作成します。
Unityではテクスチャシートアニメーションや Flipbook Animation などと呼ばれています。
左上から右下にアニメーションする一連のパターンを並べておきます。
これをUnityに読み込むとInspectorにImport Settingsが現れるので、
Texture Shapeを3Dにし、行および列の数を3にします。
またGenerate Mipmapsのチェックを外しておきます。
これを外しておかないと、スプライトを遠くに表示した際に複数のコマがブレンドされて表示されます。
Apply するとテクスチャシートが3Dテクスチャ化されます。
ここでSliceのタブを選択し、Zの値を変更するとアニメーションを確認することができます。
表示方法-Shader
続いて、3Dテクスチャに対応したシェーダーを用意します。
基本的には普通のテクスチャを表示するのと同じですが、UVに.z値があります。
また、通常はOffset.Z=0がパターンの中央値なのですが、指定しやすいように0がパターンの先頭になるようにしておきます。
Shader "Unlit/VolumeTransparentForAnim"
{
Properties
{
_Volume("Volume", 3D) = "" {}
_Scale ("Scale (x, y, z, and w=mul)", vector) = (1,1,1,1)
_Offset ("Offset (x, y, z, and w=div)", vector) = (0,0,0,1)
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
half4 vertex : POSITION;
};
struct v2f
{
half4 vertex : SV_POSITION;
half3 uv : TEXCOORD0;
};
sampler3D _Volume;
half4 _Scale;
half4 _Offset;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
half4 sclVec = v.vertex;
sclVec.xyz /= _Scale.xyz*_Scale.w;
half4 ofs = _Offset+half4(0,0,(_Offset.w-1)/2+1,0);
o.uv = sclVec.xyz + (ofs.xyz / ofs.w) + 0.5;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return tex3D(_Volume, i.uv);
}
ENDCG
SubShader
{
Tags
{
"RenderType" = "Transparent" //"Opaque" //
"Queue" = "Transparent" // "Geometry" //
}
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
ZWrite Off
Cull Back
Offset 0,0
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
上記shaderを使用したMaterialを作成し、Vokumeに3Dテクスチャを、Offset.wに分割数の9を入力します。
プレビューモデルをQuadにしておいて、Offset.Zにパターン番号を入れるとそのパターンが表示されます。
Z=1.5を入力するとパターン1と2がブレンドされて表示されるのがわかると思います。
使用方法-Script
スクリプト上から利用する場合は、マテリアルに対してSetVector("_Offset", vec);
のようにOffsetを渡せばOKです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer))]
public class Tex3DAnim : MonoBehaviour
{
[SerializeField] float m_DivNum = 9f;
[SerializeField, Range(0.1f, 32f)] float m_speedGain = 16f;
[SerializeField] bool m_isStep = false;
Material m_mat;
Vector4 m_vec;
float m_ptn;
void Start()
{
MeshRenderer mr = GetComponent<MeshRenderer>();
m_mat = mr.material;
m_vec = new Vector4(0, 0, 0, m_DivNum);
}
void Update()
{
m_ptn += Time.deltaTime * m_speedGain;
m_vec.z = (m_isStep? Mathf.Floor(m_ptn) : m_ptn);
m_mat.SetVector("_Offset", m_vec);
}
}