Posted at

【Unity】光学迷彩を実装してみる


はじめに

Shaderの勉強として光学迷彩を実装してみました(透明マントみたいなやつ)

イメージは攻殻機動隊に出てくる熱光学式迷彩


できたもの

0f39b0a58767f4a0972aa120da6d423d.gif

「スケスケだぜ!!」


方針


背景が少し歪んで見える

背景が歪むことでモデルの輪郭がうっすら見える

モデルより奥にある別のモデルも見える状態である

基本的に反射光の映り込みはしない


光学迷彩より引用


  • Grab Passで背景部分をテクスチャとしてサンプリングし、vertシェーダで頂点のuv座標をずらすことで歪みを表現する

  • うっすら視認できるよう、輪郭の部分の歪みを大きくする

  • サンプリングしたテクスチャにのみ色収差をかけて良い感じにする

  • ランダム秒おきにパルスノイズをかけてさらに良い感じにする


コード

シェーダ

Shader "Custom/Kougaku"

{
Properties
{  //歪みの大きさ
_Shift("Shift", Range(0.0, 1.0)) = 0

//ノイズのランダム化(C#でいじる)
_Amount("Distort", Float) = 0.0
_Size("Size", int) = 10
}

SubShader
{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }

GrabPass{ "_GrabPassTexture" }

Pass{

CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};

struct v2f {
half4 vertex : SV_POSITION;
half4 grabPos : TEXCOORD0;
float3 normal : TEXCOORD1;
};

sampler2D _GrabPassTexture;
float _Amount;
float _Shift;
int _Size;

v2f vert(float4 vertex : POSITION, float3 normal : NORMAL)
{
v2f o = (v2f)0;
o.vertex = UnityObjectToClipPos(vertex);
o.normal = UnityObjectToWorldNormal(normal);
//カメラからオブジェクトのベクトルを求め正規化
half3 viewDir = normalize(WorldSpaceViewDir(o.vertex));
//法線との内積を求め、フチに行くほど歪みが大きくなるようにする
float s = 1 - abs(dot(o.normal, viewDir));
// GrabPassのテクスチャをサンプリングするUV座標はComputeGrabScreenPosで求める
o.grabPos = ComputeGrabScreenPos(o.vertex + s * _Shift);

return o;
}

fixed4 frag(v2f i) : SV_Target
{
//w除算が必要
float2 uv = float2(i.grabPos.x / i.grabPos.w, i.grabPos.y / i.grabPos.w);
float3 col;

//ノイズ
float x = 2 * uv.y;
uv.x += _Amount * sin(_Size * x)*(-(x - 1)*(x - 1) + 1);

//色収差
col.r = tex2D(_GrabPassTexture, uv).r;
col.g = tex2D(_GrabPassTexture, uv - float2(0.004, 0)).g;
col.b = tex2D(_GrabPassTexture, uv - float2(0.008, 0)).b;

return float4(col,1);
}

ENDCG
}
}
}

C#スクリプト

using System.Collections;

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class NoiseController : MonoBehaviour
{
float NoiseInterval = Random.Range(5.0f, 10.0f);
float IntervalTime;

void Start()
{
//インターバル設定
NoiseInterval = Random.Range(1.0f, 3.0f);
}

IEnumerator GeneratePulseNoise()
{
//良い感じのランダム値設定
    //Random.Range(a, b)はa以上b未満
int size = Random.Range(-20, 21);
int j = Random.Range(1, 3) * 180;
int k = Random.Range(6, 11) * 10;

    //ノイズ発生後、テクスチャを元に戻す為sinが0になる値を代入
for (int i= Random.Range(-360, 1); i <= j; i += k)
{
if(i+k>j ){
i = j;
}
GetComponent<Renderer>().material.SetFloat("_Amount", 0.1f * Mathf.Sin(i * Mathf.Deg2Rad));
GetComponent<Renderer>().material.SetFloat("_Size", size);
yield return null;
}
}

void Update()
{
transform.Rotate(new Vector3(0, 0.2f, 0));
    
//ノイズをランダム秒後に発生させる
IntervalTime += Time.deltaTime;

if (IntervalTime >= NoiseInterval)
{
StartCoroutine(GeneratePulseNoise());
IntervalTime = 0;
NoiseInterval = Random.Range(1.0f, 3.0f);
}
}
}


参考


ライセンス


ユニティちゃんライセンス

この作品はユニティちゃんライセンス条項の元に提供されています