250412

lililllilillll·2025년 4월 12일

개발 일지

목록 보기
139/350

✅ What I did today


  • Udemy Course : Unity Shader
  • Project Katana


📝 Things I Learned


🏷️ Unity :: DestroyImmediate()

DestroyImmediate() : 에디터용 Destroy()

✅ 차이: Destroy() vs DestroyImmediate()

항목Destroy()DestroyImmediate()
실행 시점다음 프레임의 끝에서 파괴즉시 파괴
사용 위치런타임 주로 사용에디터 스크립트 전용
위험성상대적으로 안전높은 위험 (Undo 불가, 에디터 충돌 가능)
사용 시기대부분의 경우특수 상황 (커스텀 에디터 등)

⚠️ 주의할 점

  • DestroyImmediate()런타임에서 사용하지 말 것.
    • 오브젝트 참조가 즉시 끊기기 때문에 NullReferenceException이 쉽게 발생함.
  • 특히 씬 내부 오브젝트나 컴포넌트를 강제로 삭제하면 Unity 에디터가 충돌할 수 있음.
  • Undo 처리가 안 되므로 에디터에서 실수로 중요한 오브젝트를 삭제하면 복구 불가.

🏷️ Unity :: Rendering API Hierarchy

보통은 Mesh Renderer가 Mesh Filter를 분석하고 알아서 low level 명령으로 바꿔준다.
엄청 많은 오브젝트를 직접 batching하거나 커스텀 효과가 필요할 때 사용될 수 있음.

항목레벨목적주요 특징
GL초저수준임시 그리기OpenGL immediate-style, 성능 낮음
Graphics.DrawMesh중간일반 메시 렌더링1회성 메시 출력
Graphics.DrawMeshInstanced고효율동일 메시 반복 렌더GPU instancing 기반
Graphics.DrawProcedural고급정점/삼각형 수 직접 지정ComputeBuffer/셰이더 기반
CommandBuffer중간~고급렌더링 명령 예약정확한 타이밍 지정 가능
CustomPass (HDRP)고급사용자 정의 렌더 패스HDRP 전용
ScriptableRenderPass (URP)고급사용자 정의 렌더 패스URP 전용
Compute Shader고급GPU 연산 전용렌더링 외에도 물리/시뮬 가능

1. 🔹 GL (UnityEngine.GL)

목적

  • 즉석에서 직접 정점/텍스처 좌표 지정하여 그리기
  • 예: 디버그용 선, 사각형, UI 오버레이

특징

  • OpenGL 스타일 (GL.Begin(), GL.Vertex(), GL.End())
  • GPU 활용도 낮음, 배치 최적화 안 됨
  • 낮은 성능, 디버그/툴 전용

2. 🔹 Graphics.DrawMesh

목적

  • Mesh 하나를 특정 위치/재질로 화면에 그리기

특징

  • Graphics.DrawMesh(mesh, matrix, material, layer)
  • Unity 내부 렌더링 파이프라인을 따름
  • 빠르고 간단한 1회성 메시 출력에 적합

3. 🔹 Graphics.DrawMeshInstanced

목적

  • 같은 메시를 여러 위치에 한 번에 그리기 (Instancing)

특징

  • Graphics.DrawMeshInstanced(mesh, submeshIndex, material, matrices)
  • GPU Instancing 사용 → Draw Call 최소화
  • 1023개까지 한 번에 가능
  • 예: 숲, 군중 등

4. 🔹 Graphics.DrawProcedural

목적

  • Mesh 없이도 정점/삼각형을 GPU에서 직접 생성해 그리기

특징

  • Graphics.DrawProceduralNow(...)
  • 정점 수, 프리미티브 타입 등 직접 제어
  • 보통 ComputeBuffer + 셰이더와 함께 사용
  • 예: VFX, 입자 시스템, 볼륨 렌더링

5. 🔹 CommandBuffer

목적

  • 렌더링 명령을 예약/지연 실행하도록 구성

특징

  • CommandBuffer.DrawMesh, SetRenderTarget, ClearRenderTarget 등 가능
  • 특정 카메라에 연결해서 렌더 타이밍 직접 제어
  • 예: Depth Prepass, 커스텀 쉐도우, 블렌딩 등

6. 🔹 CustomPass (HDRP 전용)

목적

  • HDRP에서 커스텀 렌더링 패스를 삽입

특징

  • CustomPassVolume 안에서 실행됨
  • 예: 볼륨 포그, 스크린 스페이스 이펙트, 커스텀 블렌딩
  • Execute() 메서드 안에서 CommandBuffer 사용 가능

7. 🔹 ScriptableRenderPass (URP 전용)

목적

  • URP 파이프라인에 사용자 렌더 패스를 추가

특징

  • ScriptableRendererFeature와 함께 사용
  • AddRenderPasses()로 등록, Execute()에서 동작
  • 커스텀 블룸, 데칼, 쉐도우 조작 등 가능

8. 🔹 Compute Shader

목적

  • GPU 병렬 연산을 수행 (렌더링 외 작업 포함)

특징

  • .compute 파일에서 작성
  • Dispatch() 호출로 실행
  • 렌더링 외에도 물리, VFX, 시뮬레이션 등에 사용
  • GraphicsBuffer, RWTexture, StructuredBuffer 등과 연동

🔚 총정리: 언제 뭘 쓰나?

니가 하고 싶은 일써야 하는 것
디버그용 선/사각형 그리고 싶다GL
메시 하나 그리고 싶다Graphics.DrawMesh
동일한 메시를 많이 반복해서 그린다Graphics.DrawMeshInstanced
메시 없이 정점 수만으로 그린다Graphics.DrawProcedural
그리기 순서, 타이밍을 정밀 제어CommandBuffer
HDRP에서 커스텀 효과 추가CustomPass
URP에서 커스텀 효과 추가ScriptableRenderPass
GPU로 직접 연산 시키고 싶다Compute Shader


🎞️ Udemy Course : Unity Shader


Enhanced Noise

Clouds On Camera

using UnityEngine;

[ExecuteInEditMode]
public class Clouds : MonoBehaviour
{
    public Shader CloudShader;
    float MinHeight = 0.0f;
    public float MaxHeight = 5.0f;
    public float FadeDist = 2;
    public float Scale = 5;
    public float Steps = 50;
    public Texture ValueNoiseImage;
    public Transform Sun;
    Camera _Cam;

    Material _Material;

    public Material Material
    {
        get
        {
            if (_Material == null && CloudShader != null)
            {
                _Material = new Material(CloudShader);
            }

            if (_Material != null && CloudShader == null)
            {
                DestroyImmediate(_Material);
            }

            if (_Material != null && CloudShader != null && CloudShader != _Material.shader)
            {
                DestroyImmediate(_Material);
                _Material = new Material(CloudShader);
            }

            return _Material;
        }
    }

    void Start()
    {
        // 에디터에서 돌리다가 재컴파일되거나 스크립트가 붙은 상태로 Play 모드 전환될 때
        // 기존 머티리얼이 중복으로 남아있는 걸 막기 위한 안전 장치
        if (_Material)
            DestroyImmediate(_Material);
    }

    Matrix4x4 GetFrustumCorners()
    {
        Matrix4x4 frustumCorners = Matrix4x4.identity;
        Vector3[] fCorners = new Vector3[4];

        _Cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), _Cam.farClipPlane, Camera.MonoOrStereoscopicEye.Mono, fCorners);

        frustumCorners.SetRow(0, fCorners[1]);
        frustumCorners.SetRow(1, fCorners[2]);
        frustumCorners.SetRow(2, fCorners[3]);
        frustumCorners.SetRow(3, fCorners[0]);

        return frustumCorners;
    }

    [ImageEffectOpaque]
    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (_Material == null || ValueNoiseImage == null)
        {
            Graphics.Blit(source, destination);
            return;
        }

        // 이런 초기화 코드들 Start()에 안 적는 이유 : 에디터 코드는 Start()를 계속 호출하는게 아니라서
        if (_Cam == null)
            _Cam = GetComponent<Camera>();

        Material.SetTexture("_ValueNoise", ValueNoiseImage);
        if (Sun != null)
            Material.SetVector("_SunDir", -Sun.forward);
        else
            Material.SetVector("_SunDir", Vector3.up);

        Material.SetFloat("_MinHeight", MinHeight);
        Material.SetFloat("_MaxHeight", MaxHeight);
        Material.SetFloat("_FadeDist", FadeDist);
        Material.SetFloat("_Scale", Scale);
        Material.SetFloat("_Steps", Steps);

        Material.SetMatrix("_FrustumCornersWS", GetFrustumCorners());
        Material.SetMatrix("_CameraInvViewMatrix", _Cam.cameraToWorldMatrix);
        Material.SetVector("_CameraPosWS", _Cam.transform.position);

        CustomGraphicsBlit(source, destination, Material, 0);

        return;
    }

    // 구름을 스크린에 그린다
    static void CustomGraphicsBlit(RenderTexture source, RenderTexture dest, Material fxMaterial, int passNr)
    {
        RenderTexture.active = dest;

        fxMaterial.SetTexture("_MainTex", source);

        GL.PushMatrix();
        GL.LoadOrtho();

        fxMaterial.SetPass(passNr);

        GL.Begin(GL.QUADS);

        GL.MultiTexCoord2(0, 0.0f, 0.0f);
        GL.Vertex3(0.0f, 0.0f, 3.0f); // BL

        GL.MultiTexCoord2(0, 1.0f, 0.0f);
        GL.Vertex3(1.0f, 0.0f, 2.0f); // BR

        GL.MultiTexCoord2(0, 1.0f, 1.0f);
        GL.Vertex3(1.0f, 1.0f, 1.0f); // TR

        GL.MultiTexCoord2(0, 0.0f, 1.0f);
        GL.Vertex3(0.0f, 1.0f, 0.0f); // TL

        GL.End();
        GL.PopMatrix();
    }

    protected virtual void OnDisable()
    {
        if (_Material)
            DestroyImmediate(_Material);
    }
}

(_Cam은 Camera _Cam)

  • _Cam.CalculateFrustumCorners() : 카메라 frustum
  • frustum : 시야에 보이는 영역을 표현한 입체
  • new Rect(0,0,1,1) : 전체 뷰포트를 표현할 수 있음
  • _Cam.farClipPlane : 카메라에서 가장 멀리 그려지는 거리에 위치한 평면
  • Camera.MonoOrStereoscopicEye.Mono : 어느 눈의 시점인지 지정. Mono는 일반적인 단일 시점 카메라.
  • SetRow() : 행렬의 행 할당

OnRenderImage()

  • 카메라 포스트 프로세싱에 사용되는 함수
  • Unity 카메라가 씬을 렌더링한 직후에 호출됨
  • 렌더링된 결과(source)를 받아서 필터/이펙트 처리 후 destination에 출력하는 구조
  • 이 함수가 호출되려면, 스크립트가 카메라에 붙어 있어야 하고,
    해당 카메라에 depthTextureMode 또는 PostProcess 관련 설정이 있어야 함.

[ImageEffectOpaque]

  • 렌더 순서를 opaque 렌더링 이후(transparent 이전)로 설정

Graphics.Blit()

  • Blit = Bit Block Transfer : 텍스처를 복사하거나 가공해서 다른 텍스쳐로 넘기는 함수
  • Graphics.Blit(source, destination); : source 이미지를 destination으로 그냥 복사
  • Graphics.Blit(source, destination, _Material); : _Material에 연결된 셰이더를 사용해서 source를 처리한 뒤 destination에 씀


🎮 Project Katana


화면에 맞추는 방법 : Camera에 Pixel Perfect Camera 컴포넌트 추가
Cinemachine 쓰고 있으면 Cinamachine Camera에도 Cinamachine Pixel Perfect 컴포넌트 추가

그래도 화면이 안 맞춰짐 : Sprite Pivot 때문에 0,0으로 놔도 offset이 생김

카메라를 그대로 두면 속도감이 전혀 느껴지지 않는다.

TriggerEnter2D는 rigidbody continous로 설정해도 의미없음

둘 중 하나라도 트리거 체크돼있으면 CollisionEnter2D 작동 안 함

OnCollisionEnter2D에선 linearVelocity 대신 relativeVelocity 써야 함.
linearVelocity는 충돌 때문에 0됨.
relativeVelocity 쓸 땐 rb가 아니라 collision에서 가져와야 함.



profile
너 정말 **핵심**을 찔렀어

0개의 댓글