半透明になるオブジェクトの描画順がぐちゃぐちゃになる問題の解決法
オブジェクトを半透明にするには、マテリアルのRendering ModeをOpaqueからFadeかTransparentに変更する必要があります(シェーダを変えるという方法もあります)。しかし、これらのRendering Modeには、描画順が崩壊するという問題があります。
左はOpaque右はTransparentです。Transparentで描画すると奥のキューブが地面の向こう側に行って、だまし絵のようになってしまっています。カメラの角度によっては正常に見えることも多いのですが、ゲーム中でカメラが動き回れば必ずぼろが出てしまいます。通常、Transparentはオブジェクトを半透明にするときに使うRendering Modeですので、この描画順の問題は目立たないことが多いですが、例えば、「スペースバーを押したら半透明になるオブジェクトを作りたい」というときには不透明のTransparentオブジェクトを置くことになり、この問題が浮き上がってきます。この問題を根本的に解決するには、今のところシェーダーを自作するしか方法が無いようですが、先述のような「普段は不透明で稀に透明になる」という場合はスクリプトでRendering Modeを切り替えられるようにして、不透明なときはOpaque、透明なときはTransparentのようにすれば解決できます。今回はこの方法を記述します。
そもそも、Rendering ModeはStandardシェーダーの機能です。Rendering Modeを設定することにより、シェーダーの内部変数をあらかじめ決められていた値に設定してくれるという仕組みになっているらしく、スクリプトからは直接Rendering Modeをいじることができません。そのためStandardシェーダーの内部変数を一つずつ設定する必要があるのですが、これは非常に面倒です。そこで有志の方が作ってくれたスクリプトをお借りします。
まずC#のスクリプトをStandardShaderUtilsという名前で新規作成し、下記のソースコードをコピーして貼り付けます。
using UnityEngine;
public static class StandardShaderUtils
{
public enum BlendMode
{
Opaque,
Cutout,
Fade,
Transparent
}
public static void ChangeRenderMode(Material standardShaderMaterial, BlendMode blendMode)
{
switch (blendMode)
{
case BlendMode.Opaque:
standardShaderMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
standardShaderMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
standardShaderMaterial.SetInt("_ZWrite", 1);
standardShaderMaterial.DisableKeyword("_ALPHATEST_ON");
standardShaderMaterial.DisableKeyword("_ALPHABLEND_ON");
standardShaderMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
standardShaderMaterial.renderQueue = -1;
break;
case BlendMode.Cutout:
standardShaderMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
standardShaderMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
standardShaderMaterial.SetInt("_ZWrite", 1);
standardShaderMaterial.EnableKeyword("_ALPHATEST_ON");
standardShaderMaterial.DisableKeyword("_ALPHABLEND_ON");
standardShaderMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
standardShaderMaterial.renderQueue = 2450;
break;
case BlendMode.Fade:
standardShaderMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
standardShaderMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
standardShaderMaterial.SetInt("_ZWrite", 0);
standardShaderMaterial.DisableKeyword("_ALPHATEST_ON");
standardShaderMaterial.EnableKeyword("_ALPHABLEND_ON");
standardShaderMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
standardShaderMaterial.renderQueue = 3000;
break;
case BlendMode.Transparent:
standardShaderMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
standardShaderMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
standardShaderMaterial.SetInt("_ZWrite", 0);
standardShaderMaterial.DisableKeyword("_ALPHATEST_ON");
standardShaderMaterial.DisableKeyword("_ALPHABLEND_ON");
standardShaderMaterial.EnableKeyword("_ALPHAPREMULTIPLY_ON");
standardShaderMaterial.renderQueue = 3000;
break;
}
}
}
http://answers.unity3d.com/questions/1004666/change-material-rendering-mode-in-runtime.html
からの引用になります。
このスクリプトは、簡単にStandardシェーダーのRendering Modeを変更するためのスクリプトです。次に、スペースバーを押すとオブジェクトが透明になるスクリプトを書きます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FadeScript : MonoBehaviour {
public Material material;
void Start () {
material = GetComponent<Renderer>().material;
}
void Update () {
if(Input.GetKeyDown(KeyCode.Space)){
StandardShaderUtils.ChangeRenderMode(material, StandardShaderUtils.BlendMode.Transparent);
GetComponent<Renderer>().material.color = new Color(0.58f, 0.58f, 0.58f, 0.1f);
}
}
}
プログラミングは初心者なので冗長な部分があるかもしれません。Transparentの部分をFadeに変えても使うことができます。後はこれを消したいオブジェクトにドラッグ&ドロップして完成です。
左がスペースを押す前、右がスペースを押した後です。不透明な状態では正常に描画され、透明化もできています。透明化した後の描画順は相変わらずおかしいですが、これでそれなりに目立たなくはなります。