새소식

개발/Shader

[Image Effect] 유니티 블러(Blur) 이미지 효과- 02 Trail Renderer 모션벡터 획득

  • -

이번 포스팅은 
[Blur] 블러 이미지 효과- 01 기본 셰이더 (tistory.com)

 

[Blur] 텍스쳐 기반 블러 이미지 효과- 01 기본 셰이더

이번 포스트는 세 편으로 나뉩니다. 첫 편인 기본 셰이더에선, 블러 텍스쳐를 활용해 원본의 특정 영역에 원하는 방향으로 모션블러를 주는 방법을 설명하며 두 번째 편에서는, 이 셰이더를 Trail

develop-4-art.tistory.com

이전 포스팅에서 제작한 셰이더를 활용해 트레일 렌더러의 모션벡터 값을 RenderTexture에 저장하는 과정을 다룹니다.

먼저 트레일 렌더러에서 Vertex를 추가하는 특성에 대해 알아볼 필요가 있습니다.
트레일 렌더러는 min vertex distance보다 큰 변화값이 존재하면 새로운 버텍스를 배열의 앞에 추가합니다.
그 후, Time만큼 유지됐다가 삭제가 되기에, 배열의 맨 뒤에 있는 버텍스부터 사라지게 됩니다.

따라서, 기존 버텍스에 담긴 정보는 유지하고 새로운 버텍스를 추가하기 위해선 새로 추가된 버텍스나 사라진 버텍스만큼 정보를 앞 혹은 뒤로 옮기는 작업을 해야합니다.

위를 구현하기 위해 LateUpdate 안에 아래와 같은 코드를 작성했습니다.

//	Late Update를 돌때마다 TrailRenderer가 만드는 mesh를 조작하기 쉽도록 다른 mesh에 Bake한다.
_trailRenderer.BakeMesh(meshFilter.sharedMesh);

vertices = meshFilter.sharedMesh.vertices;
var deltaCount = vertices.Length - lastVertexCount;
//	정점의 개수가 늘으면, 설명과 같이 정점 인덱스와 정보를 옮긴다.
if (deltaCount > 0)
{
  for (var i = colors.Count - 1; i > 0; i--)
  {
    if (i - (deltaCount) < 0) break;
    //	기존에 존재하던 정점의 정보를 뒤로 미룬다.
  }
  for (var i = 0; i < deltaCount; i++)
  {
   //	새로 생긴 정점의 정보를 추가한다.
  }
}
else
{
  for (var i = 0; i < colors.Count; i++)
  {
  	//	정점이 줄어들어도 큰 변화는 없다.
  }
}
//	변경된 정점의 개수를 캐싱한다.
lastVertexCount = meshFilter.sharedMesh.vertexCount;

이제 각 시점별로 추가되는 정점에 정보를 추가해야 합니다. 우리 같은 경우, Vertex Color에 Trail Renderer가 움직인 방향 정보를 담을 것이기에, 과거 위치와 현재 위치에 대한 정보를 비교해 Direction을 얻어 기입합니다.

	// Vertex Color 값을 유동적으로 옮겨야 하기에, List에 담는다.	
	var colors = new List();
        meshFilter.sharedMesh.GetColors(colors);
        //	TrailRenderer가 움직인 방향을 구한다.
        dir = (pivotTrans.position - lastPos).normalized;
        //	색상은 음수값이 나오면 안되기에, 방향값을 양수만 갖도록 한다.
        dir += Vector3.one;
        
        vertices = meshFilter.sharedMesh.vertices;
        var deltaCount = vertices.Length - lastVertexCount;
        
        if (deltaCount > 0)
        {
            if (lastColors.Count == 0) lastColors = colors;
            for (var i = colors.Count - 1; i > 0; i--)
            {
                if (i - (deltaCount) < 0) break;
                //	마지막으로 기억되어있는 color의 값을 다 뒤로 미룬다.
                colors[i] = lastColors[i - (deltaCount)];
            }
            for (var i = 0; i < deltaCount; i++)
            {
                var ratio = (float)i / vertices.Length;
                var multiplier = mixCurve.Evaluate(ratio);
                //	앞서, 양수로 만들기 위해 1만큼씩 각 요소에 더했기에 반으로 나눠
                //	0~1사이의 값으로 만든다.
                var color = new Color(.5f * dir.x, .5f * dir.y, 1f, 1f);
                color = Color.Lerp(colors[i], color, multiplier);
                colors[i] = color;
            }
        }
        else
        {
            for (var i = 0; i < colors.Count; i++)
            {
                colors[i] = lastColors[i];
            }
        }
        lastVertexCount = meshFilter.sharedMesh.vertexCount;
        lastPos = pivotTrans.position;
        lastColors = colors;
        //	새로 지정된 정점 색상 값을 매쉬에 적용한다.
        meshFilter.sharedMesh.SetColors(colors);

위와 같은 코드를 통해 트레일 렌더러가 지난 영역의 모션벡터 값을 담는 매쉬를 만들어 냅니다.

풀 소스는 아래와 같습니다.

using System.Collections.Generic;
using UnityEngine;


[ExecuteAlways]
[RequireComponent(typeof(TrailRenderer))]
public class TrailWithBlurController : MonoBehaviour
{
    [SerializeField]
    private bool isInit = false;
    
    public MeshFilter meshFilter;
    public Transform pivotTrans;
    public AnimationCurve mixCurve;
    public bool invertAxis = false;
    
    private TrailRenderer _trailRenderer;
    
    private Vector3 lastPos;
    private int lastVertexCount;
    private Vector3 dir;
    private Vector3[] vertices;
    private List lastColors = new List();
    
    private void OnEnable()
    {
        if (meshFilter == null) return;
        if (pivotTrans == null) return;
        isInit = true;
        lastColors = new List();
        _trailRenderer = GetComponent();
        meshFilter.sharedMesh = new Mesh();
        lastPos = pivotTrans.position;
        lastVertexCount = meshFilter.sharedMesh.vertexCount;
        meshFilter.sharedMesh.GetColors(lastColors);
    }

    private void OnDisable()
    {
        lastColors = null;
    }

    // Update is called once per frame
    void LateUpdate()
    {
        if (!isInit) return;
        _trailRenderer.BakeMesh(meshFilter.sharedMesh);
        
        var colors = new List();
        meshFilter.sharedMesh.GetColors(colors);
        
        dir = (pivotTrans.position - lastPos).normalized;
        if (invertAxis)
        {
            var tmp = dir.y;
            dir.y = dir.x;
            dir.x = tmp;
        }
        
        dir += Vector3.one;
        
        vertices = meshFilter.sharedMesh.vertices;
        var deltaCount = vertices.Length - lastVertexCount;
        
        if (deltaCount > 0)
        {
            if (lastColors.Count == 0) lastColors = colors;
            for (var i = colors.Count - 1; i > 0; i--)
            {
                if (i - (deltaCount) < 0) break;
                colors[i] = lastColors[i - (deltaCount)];
            }
            for (var i = 0; i < deltaCount; i++)
            {
                var ratio = (float)i / vertices.Length;
                var multiplier = mixCurve.Evaluate(ratio);
                var color = new Color(.5f * dir.x, .5f * dir.y, 1f, 1f);
                color = Color.Lerp(colors[i], color, multiplier);
                colors[i] = color;
            }
        }
        else
        {
            for (var i = 0; i < colors.Count; i++)
            {
                colors[i] = lastColors[i];
            }
        }
        lastVertexCount = meshFilter.sharedMesh.vertexCount;
        lastPos = pivotTrans.position;
        lastColors = colors;
        meshFilter.sharedMesh.SetColors(colors);
    }
}

(다음 포스트 : [Image Effect] 유니티 블러(Blur) 이미지 효과- 03 Image Effect 적용 (tistory.com))

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.