2015年1月15日木曜日

【Unity】ドット絵がぼやけて表示される【Android】

アンドロイドでビルドランするとドット絵素材がぼけて表示されてしまう。

ぼけぼけです。というわけで今回はその対策について少し調べてみました。

画像のインスペクターの Format を Compressed から Truecolor に変えると直るという情報を得たのでやってみました。

…が、直りませんでした。

XperiaA2の解像度はHD : 1280×720 なので 16×16 のウサギは小さすぎるのでは?と思いサイズを10倍にしたところきれいに表示されました。

真ん中にいるちょっときれいなウサギだけ160×160のウサギです。
拡大はMicrosoftのペイントを、透過処理はEDGEを使いました。
ちなみに160×160のウサギの Format を Compressed、Truecolorと変えてみましたが大きな違いは見られませんでした。

それから 64×64 の2Dドットユニティちゃんも試してみたんですが微妙にぼやけました。
スマホできれいに表示するにはけっこう大きいサイズにする必要があるみたいです。

PCのエディタ上ではさほど気にならないんですけどね…。でも比較するとエディタ上でも違いますね。元サイズが大きい画像の方がやはりきれいに映ります。


うーん…でも16×16の画像をきれいに映す方法はあるんじゃないかなぁ…



<追記>
某所で有益な情報を教えて頂きました。

素材のインスペクタにある Filter Mode を Point に変更するとテクスチャ補完が線形補完から近傍点になってドットの滲みは解消できる。
ただ、表示座標がきっちり液晶のピクセルの座標になっていないと波打つように伸縮するので注意。

…ということでしたのでやってみました。
左:16×16 FilterMode Point
右:160×160 FilterMode Bilinear

拡大。中間色が全くありません。若干色味が明るくなっているような…

Sceneビューでの表示がPersepectiveなのでカメラから見てオブジェクトが少し斜めになっています。その結果ドットが少し荒れています。

実機に落とし込んでみます。

滲みは全くありませんでした。ぼやけてないです。
ただやはり元画像が小さいとダメなようです。


FilterMode Point はピクセルを使った表現をする場合とても使えそうです。
ピクセルをキッカリ描画できる。これは利点です。
ただオブジェクトが斜めに傾いていたり0.5ピクセルずれが起こるとドット絵が荒れるので、描画するスクリーンの大きさがあらかじめ決まっている、もしくは指定できる環境でのみ力を発揮するのではないかと思われます。

【Unity】Android実機でOpenGL描画ができない?シェーダが機能しない?


こいつがアンドロイド実機でどれくらい動くのか少しテストをしてみたんです。
そうしたら実機では前方にあるキューブ以外きれいさっぱり消えてしまった。
エディタ上では問題なく表示されていたし、UnityRemote4でも問題はなかったのに…

消えてしまったのはOpenGLで描画されたものでした。
ということで似たような状況を再現、原因を調べました。


新しいシーンを用意、下記2つのプログラムをカメラにアタッチ。
シーンにはカメラ以外何もありません。

using UnityEngine;
using System.Collections;

public class GLLine2 : MonoBehaviour
{
    private Material m_lineMaterial;
    public int _fieldSide;
    
    Vector3[] _vertices;
    public Color _color;
    
    void Start ()
    {
        // ライン描画用のマテリアルを生成.//
        m_lineMaterial = new Material(
        "Shader \"myShader\" {" +
        "SubShader {" +
        "Pass {" +
        " ZWrite Off" +
        "Blend SrcAlpha One" +
        " Cull Off" + 
        " BindChannels {" +
        " Bind \"vertex\", vertex Bind \"color\", color" +
        " }" +
        "}" +
        "}" +
        "}"
        );//material end

        m_lineMaterial.hideFlags = HideFlags.HideAndDontSave;
        m_lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave;

        //各頂点の座標、初期位置決定
        _vertices = new Vector3[_fieldSide*_fieldSide];
        for(int i=0; i<_fieldSide; i++)
        {
            for(int j=0; j<_fieldSide; j++)
            {
                _vertices[j + _fieldSide * i] = new Vector3(j,0,i);
            }
        }
    }

    void OnRenderObject() {
        if (m_lineMaterial != null) {
        m_lineMaterial.SetPass(0);
        GL.PushMatrix();
        GL.Begin(GL.LINES);
        GL.Color(_color);
        for(int i=0; i<_fieldSide; i++)
        {
            for(int j=0; j<_fieldSide - 1; j++)
            {
                GL.Vertex3(_vertices[j + i*_fieldSide].x, _vertices[j+i*_fieldSide].y, _vertices[j+i*_fieldSide].z);
                GL.Vertex3(_vertices[j+1 +i*_fieldSide].x, _vertices[j+1 +i*_fieldSide].y, _vertices[j+1 +i*_fieldSide].z);
            }
        }
        for(int i=0; i<_fieldSide; i++)
        {
            for(int j=0; j<_fieldSide - 1; j++)
            {
                GL.Vertex3(_vertices[i + j*_fieldSide].x, _vertices[i + j*_fieldSide].y, _vertices[i + j*_fieldSide].z);
                GL.Vertex3(_vertices[i + (j+1)*_fieldSide].x, _vertices[i + (j+1)*_fieldSide].y, _vertices[i + (j+1)*_fieldSide].z);
            }
        }
        GL.End();
        GL.PopMatrix();
        }
    }
}

using UnityEngine;
using System.Collections;

public class TestOpenGL2android : MonoBehaviour
{
    private Material m_material;
    
    void Start ()
        {
        // マテリアルの生成.
        m_material = new Material(
        "Shader \"myShader\" {" +
        "SubShader {" +
        "Pass {" +
        " ZWrite Off" +
        " Cull Off" + 
        " BindChannels {" +
        " Bind \"vertex\", vertex Bind \"color\", color" +
        " }" +
        "}" +
        "}" +
        "}"
        );

        m_material.hideFlags = HideFlags.HideAndDontSave;
        m_material.shader.hideFlags = HideFlags.HideAndDontSave;
    }

    void OnPostRender() 
    {
        if (m_material != null) 
        {
        m_material.SetPass(0);// マテリアルのパス0を割り当て.
        GL.PushMatrix();
        GL.LoadOrtho(); // 2D描画として処理.
        GL.Begin(GL.TRIANGLES);
        GL.Color(Color.red);
        GL.Vertex3( 0.0f, 0.0f, 0.0f );
        GL.Color(Color.green);
        GL.Vertex3(0.0f, 1.0f, 0.0f );
        GL.Color(Color.blue);
        GL.Vertex3(0.5f, 0.5f, 0.0f );
        GL.End();
        GL.PopMatrix();
        }
    }
}

2つ目のプログラムはここのものを参考に、もといまるぱくりです。本当にありがとうございます。
なぜ2つ用意したのかというと OnRenderObject() と OnPostRender() のどちらかが原因になっているかもしれないと思ったからです。
実機に落とし込みます。

正常ですね。
しかしキューブを置くと…

暗くなります。ラインの格子は消えます。
しかし色こそ着いていないものの OpenGL で書かれたトライアングルは消えていません。

ということは OpenGL の描画はできている。ラインも描かれている。
シェーダかマテリアルか、その辺に原因はありそうです。

もう少し調べます。
今度は画面端にキューブを置いてみます。エディア上ではこんなかんじ。

実機

画面内にキューブが入るとキューブ以外のシェーダが機能しなくなるようです。
シーンの中のキューブの有無が直接関係しているわけではないのですね。

1つ目のプログラム、Start内のシェーダをコメントアウトしてみます。

ラインが出てきました。
色についてはやはりキューブが画面内におさまっていると真っ黒になってしまいます。



<追記>
GL.LINES による描画は行われていて、スクリプト内に書かれたシェーダが標準のシェーダと同一画面に収まった時に何らかの理由から機能しなくなるというので正解のようです。
ちなみに最初に GL.LINES によるピンクの格子が消えてしまったのはアルファブレンディングの設定が Blend SrcAlpha One となっていたからでした。
画面左のトライアングルについても同様のアルファブレンディングを指定したところ、やはり同じように消えてしまいました。

解決策は一応見つかった…んですかね。とりあえずこれを見ていただきたい。

Sprite Renderer を持つウサギを描画画面に入れるとスクリプトで割り当てたシェーダの機能が復活、もとい正しく機能するんです。

デフォルトのキューブもしっかり入っています。


以下、他にわかった事を。

実機上では線の色を変更する場合、マテリアルを割り当てる必要がある。
割り当ててるんだけどな…ということでマテリアルを割り当てない場合はどうなるか。
上で示した GLLine2.cs のマテリアル割り当てに関わる部分(m_lineMaterialとシェーダ)をコメントアウトしてみると…

確かにこれはダメですね。
ちなみに画面内にSpriteRendererを持つオブジェクトがあるとこの格子は表示されなくなります。

混迷を極めてまいりました。

マテリアル割り当てる → Gameビューでは問題なく表示される。実機では SpriteRenderer の存在により割り当てたシェーダが機能する。

マテリアル割り当てない → Gameビューでデフォルトのシェーダの有無により割り当てたシェーダが機能しなくなる。かつ、SpriteRenderer の存在により割り当てたシェーダが機能しないどころかOpenGLによる描画すら消す(もしくは透明になっている)

そろそろ勘弁していただきたいのですが…

とりあえず GLLine2.cs のマテリアル割り当ては元に戻します。
続いてもう1つスクリプトを追加します。
using UnityEngine;
using System.Collections;

public class DebugLine : MonoBehaviour {

    // line material
    private Material    m_LineMaterial;

    void Awake()
    {
        m_LineMaterial = createLineMaterial( Color.white );
    }

    void OnRenderObject()
    {
        Vector3 vPos    = Vector3.zero;
        Quaternion qRot = Quaternion.identity;
        Matrix4x4 mtx   = Matrix4x4.TRS( vPos, qRot, Vector3.one );

        GL.PushMatrix();
        {
            GL.MultMatrix( mtx );

            m_LineMaterial.color = Color.yellow;
            m_LineMaterial.SetPass( 0 );
            
            GL.Begin( GL.LINES );
            {
                GL.Vertex( new Vector3(0,0,0) );
                GL.Vertex( new Vector3(0,0,10) );
            }
            GL.End();

            // change color
            m_LineMaterial.color = Color.cyan;
            m_LineMaterial.SetPass( 0 );

            GL.Begin( GL.LINES );
            {
                GL.Vertex( new Vector3(2,0,0) );
                GL.Vertex( new Vector3(2,0,10) );
            }
            GL.End();
        }
        GL.PopMatrix();
    }

    private Material    createLineMaterial( Color color )
    {
        return new Material(
            "Shader \"Lines/Background\" {"+
            "    Properties { "+
            "        _Color (\"Main Color\", Color) = ("+color.r+","+color.g+","+color.b+","+color.a+")"+
            "    }"+
            "    SubShader {"+
            "        Pass {"+
            "            ZWrite on "+
            "            Blend SrcAlpha OneMinusSrcAlpha "+
            "            Colormask RGBA "+
            "            Lighting Off "+
            "            Offset 1, 1 "+
            "            Color[_Color] "+
            "        }"+
            "    }"+
            "}"
            );
    }
}

実機に落とし込みます。縦、



追加したスクリプトが描くラインはデフォルトシェーダと競合していません。(競合って表現は正しいのだろうか?)
DebugLine.cs のマテリアルを GLLine2.cs のマテリアルに近づけていきます。

ZWrite on → ZWrite Off に変更
Blend SrcAlpha OneMinusSrcAlpha → Blend SrcALpha One に変更
BindChannels の追加

ここらへんまでやっても変化が全くありません。ブレンディングを変えたからサチるようにはなりましたが…

Colormask RGBA
Lighting Off
Offset 1, 1

それぞれを消して実機で動作確認を繰り返しても変化なし。(7パターン)

どうもシェーダ内に Color[_Color] がないのが原因な気がしてきました。
GLLine2.cs では GL.Color(_color) としていて _color の値はmetaファイルから読み込んでね!としているのがまずいのではないかと…

最終的にGLLine2.csはこうなりました。
using UnityEngine;
using System.Collections;

public class GLLine2 : MonoBehaviour
{
    private Material m_lineMaterial;
    public int _fieldSide = 50;
    Vector3[] _vertices;
    //public Color _color;

    void Start ()
    {
        // ライン描画用のマテリアルを生成.//
        m_lineMaterial = createLineMaterial(Color.white);
        m_lineMaterial.hideFlags = HideFlags.HideAndDontSave;
        m_lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave;

        //各頂点の座標、初期位置決定
        _vertices = new Vector3[_fieldSide*_fieldSide];
        for(int i=0; i<_fieldSide; i++)
        {
            for(int j=0; j<_fieldSide; j++)
            {
            _vertices[j + _fieldSide * i] = new Vector3(j,0,i);
            }
        }
    }

    void OnRenderObject() {
        if (m_lineMaterial != null) {
        m_lineMaterial.SetPass(0);//setPassはShaderのPass,2passの内1Pass目なら0
        GL.PushMatrix();
        GL.Begin(GL.LINES);

        //GL.Color(_color);//競合で消える
        m_lineMaterial.color = Color.cyan;//反映されるのはcyanのみ
        m_lineMaterial.SetPass( 0 );

        for(int i=0; i<_fieldSide; i++)
        {
            for(int j=0; j<_fieldSide - 1; j++)
            {
                GL.Vertex3(_vertices[j + i*_fieldSide].x, _vertices[j+i*_fieldSide].y, _vertices[j+i*_fieldSide].z);
                GL.Vertex3(_vertices[j+1 +i*_fieldSide].x, _vertices[j+1 +i*_fieldSide].y, _vertices[j+1 +i*_fieldSide].z);
            }
        }

        //GL.Color(Color.yellow);//競合で消える
        m_lineMaterial.color = Color.magenta;
        m_lineMaterial.SetPass( 0 );

        for(int i=0; i<_fieldSide; i++)
        {
            for(int j=0; j<_fieldSide - 1; j++)
            {
                GL.Vertex3(_vertices[i + j*_fieldSide].x, _vertices[i + j*_fieldSide].y, _vertices[i + j*_fieldSide].z);
                GL.Vertex3(_vertices[i + (j+1)*_fieldSide].x, _vertices[i + (j+1)*_fieldSide].y, _vertices[i + (j+1)*_fieldSide].z);
            }
        }

        GL.End();
        GL.PopMatrix();
        }//if end
    }

    private Material createLineMaterial( Color color )
    {
        return new Material(
            "Shader \"myShader\" {" +
            "Properties { "+
            "_Color (\"Main Color\", Color) =  ("+color.r+","+color.g+","+color.b+","+color.a+")"+
            "}"+
            "SubShader {" +
            "Pass {" +
            " ZWrite Off" +
            "Blend SrcAlpha One" +
            " Cull Off" +
            " Color[_Color]"+
            " BindChannels {" +
            " Bind \"vertex\", vertex Bind \"color\", color" +
            " }" +
            "}" +
            "}" +
            "}"
        );//material end
    }
}

Sceneビュー及びGameビュー

実機縦

実機横

実機ではこちらの狙い通り、横線にシアン、縦線にマゼンタを適用できています。
しかしエディタ上では縦横ともにマゼンタになってしまっています。(シェーダエラーじゃないですよ。シアン一色にもできました。)
このやり方でエディタに2色表示させるにはシェーダに2Pass目が必要なのかもしれません。


まとめると、

GL.Color(Color.yellow);
GL.Color(_color); //_colorはpublic指定、インスペクタで値を設定、その値はmetaファイルに保持される。

のようなやり方だと実機でデフォルトシェーダに負け、色が消える。
アルファブレンディングの設定によっては線も消える。(アルファ値ゼロとして扱われるからか?)
実機でデフォルトシェーダと共存するには

割り当てるマテリアル(シェーダ)内に Color を用意
m_lineMaterial.color = Color.magenta;
m_lineMaterial.SetPass(0);

割り当てたマテリアルの持つ変数 .color を変えるとうまくいくようです。


こんなところですかね。
しかしここまで実機とエディタで差が出るとは… シェーダは怖いですね。
最後にひとつ忘れていたので… 使用した端末は Xperia A2 です。他の端末でどうなるかは保証しかねます。これ最初に言うべきだったな…



参考にさせていただいたサイト様はこちら。
  ・Post Position : 【Unity】GLを使ってデバッグ用のライン描画
  ・OnionGinger : Unity GL (1) ライン描画




2015年1月13日火曜日

【Unity】 CSVとの連携

CSVファイルを使ってフィールドを作成したいと思ったのでCSVファイルとの連携について書きます。今回は読み込みのみ。

まずCSVファイルを用意します。今回は50×50、計2500個のデータを有するCSVファイルを用意しました。

用意したCSVファイルの拡張を.txtに変え、Assets/Resources の中に入れます。Resourcesフォルダがない場合は作ってください。

テキストにするとカンマが入ります。

続いてプログラムはこちら
using UnityEngine;
using System.Collections;

public class CSVReader : MonoBehaviour {
    int _size = 50;
    string[,] _box;

    void Start () 
    {
        _box = new string[_size, _size];

        //テキストファイルの読み込み
        TextAsset _fieldTxt = Resources.Load("CSV/field0", typeof(TextAsset)) as TextAsset;
        //区切り条件指定
        //char[] _kugiri = {'\r','\n'};//改行区切り、二種類
        char[] _kugiri = {'\n'};
        //String配列に格納
        string[] _fieldString = _fieldTxt.text.Split(_kugiri);

        for(int i = 0; i < _size; i++)
        {
            string[] _tempLetter = _fieldString[i].Split(',');

            for(int j=0; j < _size ; j++)
            {
                _box[j, i] = _tempLetter[j];
                Debug.Log (j+":"+i+" ; " + _box[j,i]);
            }
        }
    }
}

再生するとコンソールはこんな感じになります。

ちゃんとCSVファイルからデータを読み込み、stringの配列に格納できていますね。
このままではstring型なのでint.Parseなどでint型に変換してフィールドに落とし込みます。

CSVデータ適用前

適用後


上に書いたプログラムはtempLetter[]なんて使ってるあたり酷いし必ずもっといいやり方があるはずなので探してみてください。というかどなたかやり方教えてください。

//char[] _kugiri = {'\r','\n'};//改行区切り、二種類
がコメントアウトされているのは50×50のデータを1本のストリングで扱おうとした時の名残りです。
'\r' をカンマだと思っていたんですがそもそもそれが間違っていて、カンマを同時に指定する方法がなかなか見つからなかったので逃げに走りました。


以下私的メモ


■ Error CS0101

error CS0101: The namespace `global::' already contains a definition for `GLLine'

同じ名前のスクリプトがあるときに出るエラー。上記エラーの場合、

public class GLLine : MonoBehaviour { ~~ }

これがプロジェクト内に2つあります。
スクリプトをコピーして名前を変え忘れてるとかそんなところだと思います。

2015年1月4日日曜日

【Unity】MeshTopology.Lines と vertices

前回 MeshTopology.Quads をいじっている過程で MeshTopology.Lines が使えるなと思い、これでピラミッドが作りたいなと思ったので作ってみました。

ちなみにこれが今までのピラミッドです。

トポロジーを Lines にするとこうなります。

頂点数65535個という上限でこんな頂点の使い方をしているとフィールドが狭くなってしまうし、プログラムが面倒になるしであまり良いことがないのでガッツリ削り取りたいんですよね。
正直四角メッシュが使えればこんなことで悩む必要なかったんですが…

とりあえずFieldを作ってみます。

ここまでは問題なくできました。
あとは頂点の座標をいじり隆起させるだけです。

できませんでした。
確かに指定した頂点座標は変化していました。しかしメッシュは一向に動かず。

こんな風に動いてくれるはずだったんですが…

トポロジーを Lines にしたことか、もしくは triangles を完全放棄してるのが原因かな?少し検証します。



<追記>

できました。どうも再描画する時に

_mesh.vertices = _vertices;
GetComponent<MeshFilter>().mesh = _mesh;
_meshFilter.mesh.SetIndices(MakeIndices(), MeshTopology.Lines, 0);

この3つをセットでやらなければならないようです。
デフォルトのトポロジーなら一行目だけで済んだんですが少しやることが増えているみたい。

_meshFilter.mesh.SetIndices(MakeIndices(), MeshTopology.Lines, 0);
とか必要ない気もするんですけどねぇ…でもこれ入れないと頂点動かした瞬間メッシュが全部消えてしまうんですよね。



<追追記>
このやり方、mesh をどんどん書き換えるわけですが Gameビューの Statisticsを 見てもらえればわかる通りえらいメモリを喰います。
書き換える mesh が小さいものならいいんですが頂点数が大きいものだとあっという間に VRAM usage が跳ね上がり、1GB超えたあたりでフリーズもしくはクラッシュしますので気をつけてください。

Lineを引きたいだけならOpenGLを直接叩いた方が軽いようなので自分はそうしました。

2015年1月3日土曜日

【Unity】MeshTopology.Quads, 四角ポリゴンについて

四角ポリゴンがほしい(切実)

UnityはOpenGLが使えるはずなのになんで四角ポリゴンが使えないの?
読み込まれた四角ポリゴンは勝手に三角ポリゴンにされてしまいます。

↓三角ポリゴンにされたパンダくん。こいつはBlender上では四角ポリゴンで作られていたのだ。


今回は MeshTopology なるものをなんとかすれば四角ポリゴンが使えないかな?と思って試行錯誤し、その過程で得られたものをまとめることにします。


超参考: 自習室 UnityでありもののMeshをワイヤーフレームで描画する


すばらしいですね。
MeshTopology.Lines を使うことでプリミティブな Sphere をここまでかっこよくすることができるとは…
このプログラムについては自習室さんの方で丁寧に解説されているのでそちらを参考にしてください。

今回はあくまでMeshTopology.Quadsが使えるか使えないかということのみに焦点を絞ります。
参考にしてつくったプログラムがこちら

using UnityEngine;
using System.Collections;

public class MakeQuads : MonoBehaviour {

    Mesh _mesh;
    private MeshFilter _meshFilter;
    private Vector3[] _newVertices;

    void Awake()
    {
        //Reference
        _meshFilter = GetComponent<MeshFilter>();
    }

    void Start () 
    {
        _mesh = new Mesh();
        _newVertices = new Vector3[4];
        Vector2[] _newUV = new Vector2[4];

        //Set vertices
        _newVertices[0]=new Vector3(0,0,0);
        _newVertices[1]=new Vector3(1,0,0);
        _newVertices[2]=new Vector3(0,0,1);
        _newVertices[3]=new Vector3(1,0,1);

        //Set UV
        _newUV[0]= new Vector2(0,0);
        _newUV[1]= new Vector2(1,0);
        _newUV[2]= new Vector2(0,1);
        _newUV[3]= new Vector2(1,1);

        _mesh.vertices = _newVertices;
        _mesh.uv = _newUV;

        //Meshの設定、名前付け
        GetComponent<MeshFilter>().mesh = _mesh;
        GetComponent<MeshFilter>().mesh.name = "LineQuadMesh";

        _meshFilter.mesh.SetIndices(MakeIndices(), MeshTopology.Quads, 0);

        //再計算、必要?SetIndicesの前でも後でも表示は変わらなかった。
        _mesh.RecalculateNormals();
        _mesh.RecalculateBounds();

        //MeshTopologyの確認
        MeshTopology topo = _meshFilter.mesh.GetTopology(0);
        Debug.Log(topo); // Quads と出力されるはず
    }

    int[] MakeIndices()
    {
        int[] indices = new int[4];

        indices[0]=0;
        indices[1]=2;
        indices[2]=3;
        indices[3]=1;

        return indices;
    }
}

こいつを空のオブジェクトにアタッチ。
それから Mesh Filter と Mesh Renderer のコンポーネントを持たせて再生してみてください。

ピンクの四角いオブジェクトが出ると思います。
ちゃんと四角い形です。コンソールにはQuadsの文字が出ているはずです。
MeshTopology.Quadsを使う ということは一応成功しているわけですね。

しかしワイヤーフレームを表示するとわかりますが三角ポリゴンで構成されています。

ピンクなのはマテリアルを持たせていないからなので適当にマテリアルを作って貼り付ければ問題ありません。ついでに画像を用意してテクスチャとして貼ってみましょう。


エディタで作れるQuadと大差ないですね。
ということで MeshTopology.Quads では四角ポリゴンは作れないという結果で終わってしまいました。
MeshTopology.Quads はテッセレーションシェーディングで使う以外使い道はなさそうです。

以下メモのようなもの

MakeIndices() におけるインデックス番号の設定の仕方によってワイヤーフレームの表示、表示される面の向きが変わります。
時計周り、時計の反対周り、Z字など試した結果、時計周りに配置すれば上から見えることがわかりました。



このまま終わるのは悔しいので擬似的に作ってみました。

左側がエディタで作った Quad 、右側が擬似です。
各頂点の頂点座標を指定し、トポロジーを Lines にしてつなげたものになります。頂点数は4、インデックスは8になります。
四角ポリゴンっぽく見えます。が、面はありませんので面にテクスチャを貼ることはできません。

一応トポロジーが Lines でもテクスチャ貼ることはできるんですがこうなります。

右は普通の Sphere ですのでそのトポロジーは Triangles になります。

2015年1月1日木曜日

テスト


【Unity】Shaderはじめました。その3.5

あけましておめでとうございます。2015年になってしまいました。

前回のその3にて頂点色を表示するシェーダというものがありました。

オブジェクトが真っ白であまりにも味気ないのと、結局頂点色ってなんなの?という思いがありましたので追記ということで少しやっていくことにします。


【Vertex color】


Shader !Debug/Vertex color {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // vertex input: position, color
            struct appdata {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.color = v.color;
                return o;
            }

            fixed4 frag (v2f i) : COLOR0 { return i.color; }
            ENDCG
        }
    }
}

シェーダそのものは前回と同じものを使用します。相変わらず真っ白ですね。
中央のキューブはそのまま、左右のキューブはエディタでスケールを変えたものになります。
今回はオブジェクトに以下のスクリプトをアタッチします。

using UnityEngine;
using System.Collections;

public class vcolor : MonoBehaviour {

    void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        int[] triangles = mesh.triangles;
        Color[] colors = new Color[vertices.Length];
        int i = 0;

        while (i < vertices.Length)
        {
            colors[i] = Color.Lerp(Color.red, Color.green, vertices[i].y);
            i++;
        }
        mesh.colors = colors;

        //頂点位置の確認
        for(int j = 0; j < vertices.Length; j++)
        {
            Debug.Log ("vertices " + j + " : "+ vertices[j]);
        }

        //triangles確認
        for(int j = 0; j < vertices.Length; j++)
        {
            Debug.Log ("triangles " + j + " : "+ triangles[j]);
        }
    }
}

頂点位置とtrianglesの確認は消していただいてかまいません。
while文とその周りが重要です。再生するとこのようになります。


大したことはしていません。順を追ってやっていきましょう。

■Mesh mesh = GetComponent<MeshFilter>().mesh;
MeshFilterコンポーネントの参照をします。
まずここで参照しないと続く .vertices や .colors などのMeshクラスの持つ変数が利用できないので必ずしてください。

■Color[] colors = new Color[vertices.Length];
頂点の数だけ色を用意したいので配列を作り、領域を確保します。
vertices.Length が頂点の数になります。Vector3[] vertices の配列の長さを返しているわけです。

■colors[i] = Color.Lerp(Color.red, Color.green, vertices[i].y);
while文を使って全頂点に適用しています。
頂点のy座標が0なら赤、1以上なら緑になるように色を設定しています。
Lerp()を使うことでグラデーションを表現しています。

この3つのオブジェクト、色の変化が似ていると思いませんか?
明らかにy座標違うじゃん!もっと色に違いがあるべきなんじゃないの?!と思うのが自然です。

キューブの頂点らは以下のようになっています。


黒文字で書かれているのが頂点座標です。Vector3 vertices[n] にあたるものです。
その絶対値は全て0.5です。エディタ上でオブジェクトのスケールをどんなに変えてもこの値が変わることはありませんでした。
なので大きさが違ったり形が違ったりしても色が同じように付くのです。

もう少し実験してみましょう。先ほどまでアタッチしていたスクリプトをはずし、新たに次のスクリプトをアタッチします。

using UnityEngine;
using System.Collections;

public class vcolor2 : MonoBehaviour 
{

    // Use this for initialization
    void Start () 
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        Color[] colors = new Color[vertices.Length];

        //個別に設定
        colors[5] = Color.red;
        colors[6] = Color.blue;
        colors[12] = Color.green;
        colors[20] = Color.yellow;

        mesh.colors = colors;
    }
}

再生するとこのように色が付きます。

greenはどこにあるのかというと下の面にあります。


色を指定した頂点の周りには色がついています。また指定していない部分は黒くなっています。
色のつく範囲ですが、色指定された頂点のある triangle上のみに限られています。
これはこのキューブが12枚の triangle で構成されているからです。
黄色に染まっている面と青に染まっている面は見かけ上つながっていますが実際にはつながっていないということです。
見かけ上このキューブは8個の頂点を持っています。しかし実際は24個の頂点を持っているのです。
そして同じ座標にある vertices[6], vertices[12], vertices[20] はそれぞれ独立していて連動しません。vertices[6] を動かしても他の2つは微動だにしません。

もう少し実験しましょう。
次はコイツです。色指定する頂点以外の頂点をすべて透明にします。

using UnityEngine;
using System.Collections;

public class vcolor2 : MonoBehaviour 
{
// Use this for initialization
    void Start () 
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        Color[] colors = new Color[vertices.Length];

        for(int i = 0; i < vertices.Length; i++)
        {
            colors[i] = Color.clear;
        }

        //個別に設定
        colors[5] = Color.red;
        colors[6] = Color.blue;
        colors[12] = Color.green;
        colors[20] = Color.yellow;
        mesh.colors = colors;
    }
}

これで透明に…ならない?!

あれ?おかしいな…と思って色々いじってみたんですが状況は

・頂点色無指定の状態だと真っ白
・何か一つでも指定すれば指定していないものはRGBA全て 0.0 となり黒く表示される
・Alpha 0.0 でも透明にならない

というところから動かず。
各頂点の alpha はしっかり 0.0 になっているのでこれはもうシェーダの問題だ。
Vertex color はそういうシェーダなんだ。

そんなことで片づけてしまうのは非常に悔しいのですがシェーダの中身をいじっても透明にならないので打ち切ります。


ざんねん わたしの ぼうけんは これで おわってしまった




<追記>




すばらしい。すばらしいぞ。
やったことはシェーダのPassの頭の部分(Fogの真上)に
    ZWrite Off
    Blend SrcAlpha OneMinusSrcAlph
の2行を追加しただけ。
標準的なアルファブレンディングだそうです。
ブレンディングについてはここ