Mugichoko's blog

Mugichoko’s blog

プログラミングを中心としたメモ書き.

Unity + GLSLで2D Array Textureを使う

モチベーションと目標

前回(Unity + GLSLで独自メッシュにテクスチャを張る)は,そのタイトルの通りGLSLでテクスチャマッピングを行いました.今回は,GLSLで2D Array Textureを使ってみたいと思います.だんだんニッチな内容になってきたように思いますが,将来的に必要になると思うので,自分の備忘録のために書き残します.

テクスチャを用意する際の注意点

少し調べてみると,以下のような記述を発見.

Sadly Unity does not support creating Texture Arrays in the editor so we have to do it in code. Below is an example using an array of ordinary Texture2D’s to create the Texture2DArray. — How To Use Texture Arrays In Unity – Caleb Faith – Medium

ということでして,Texture ArraysをUnityのエディタ上で直接与えることができないようです.ということで,一つの方法として,C#のコード内でテクスチャを1枚ずつ読み込み,それをまとめてTexture Arrayとして用意する方法を実装することに.

実装内容

方針

前回の実装を拡張していく.

  1. 2枚のテクスチャをC#コード内で読み込み,ひとまとめにしてTexture Arrayとして用意する
  2. UniformでtextureID変数を用意し,これを実行時に変更することで,矩形に表示されるテクスチャを切り替える

C#コード

  • 7行目 Editor内のInspectorに表示されるようにpublictextureID変数を作成する
  • 35~37行目 画像をファイルから読み込む
    • lena.pngmandrill.pngAssets/Resources/Texturesに事前に入れておきます
  • 39~46行目 Texture2DArray()を用いてTexture Arrayを作成し,先の2枚の画像をTexture Arrayに入れる
  • 48~49行目 GLSLのShader内の名前と紐づける
  • 55~56行目 textureID変数がEditor内で変更されるのでそれをShader内に反映させる
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class myMesh : MonoBehaviour
{
    public int textureID;

    // Start is called before the first frame update
    void Start()
    {
        var mf = GetComponent<MeshFilter>();
        var mesh= new Mesh();
        mf.mesh = mesh;

        var vertices = new Vector3[4];
        vertices[0] = new Vector3(0.0f, 0.0f, 0.0f);
        vertices[1] = new Vector3(1.0f, 0.0f, 0.0f);
        vertices[2] = new Vector3(0.0f, 1.0f, 0.0f);
        vertices[3] = new Vector3(1.0f, 1.0f, 0.0f);
        mesh.vertices = vertices;

        var indices = new int[6];
        indices[0] = 0; indices[1] = 2; indices[2] = 1;
        indices[3] = 2; indices[4] = 3; indices[5] = 1;
        mesh.triangles = indices;

        var uv = new Vector2[4];
        uv[0] = new Vector2(0, 0);
        uv[1] = new Vector2(1, 0);
        uv[2] = new Vector2(0, 1);
        uv[3] = new Vector2(1, 1);
        mesh.uv = uv;

        var texture = new Texture2D[2];
        texture[0] = Resources.Load<Texture2D>("Textures/lena");
        texture[1] = Resources.Load<Texture2D>("Textures/mandrill");

        var texArr = new Texture2DArray(texture[0].width, texture[0].height, texture.Length, TextureFormat.RGBA32, true);
        texArr.filterMode = FilterMode.Bilinear;
        texArr.wrapMode = TextureWrapMode.Clamp;
        for (int idx = 0; idx < texture.Length; ++idx)
        {
            texArr.SetPixels(texture[idx].GetPixels(0), idx, 0);
        }
        texArr.Apply();

        Renderer renderer = GetComponent<Renderer>();
        renderer.material.SetTexture("texArrSampler", texArr);
    }

    // Update is called once per frame
    void Update()
    {
        Renderer renderer = GetComponent<Renderer>();
        renderer.material.SetInt("textureID", textureID);
    }
}

GLSLコード

Shader "GLSL basic shader" {
    SubShader{
        Pass {
            GLSLPROGRAM

           #ifdef VERTEX

            uniform int textureID;

            out vec2 vsTexCoord;
            flat out int vsTexID;

            void main()
            {
                vsTexCoord = gl_MultiTexCoord0.xy;
                vsTexID = textureID;

                gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
            }

           #endif

           #ifdef FRAGMENT

            uniform sampler2DArray texArrSampler;

            in vec2 vsTexCoord;
            flat in int vsTexID;

            void main()
            {
                gl_FragColor = texture(texArrSampler, vec3(vsTexCoord, vsTexID));
            }

           #endif

            ENDGLSL
        }
    }
}

描画結果

以下の2つの図にあるように,UniformのtextureIDを描画時に切り替えることで2枚のテクスチャを切り替えてみた.

f:id:Mugichoko:20190212001138p:plain
Lenaを表示 (textureID =0)

f:id:Mugichoko:20190212001210p:plain
Mandrillを表示 (textureID = 1)

ちゃんと切り替わっています.これで今回の目標は達成です.