250119

RTFM·2025년 1월 19일

개발 일지

목록 보기
56/350

✅ 오늘 한 일


  • 대마왕의 유니티 URP 셰이더 그래프 읽기
  • 헤드 퍼스트 디자인 패턴 읽기


📝 배운 것들


🏷️ Unity : Plane과 Quad의 차이

주요 차이점 비교

특징PlaneQuad
구성10x10으로 분할된 그리드단순한 1개의 사각형(2개의 삼각형)
폴리곤 수200개의 삼각형2개의 삼각형
기본 크기10x10 Unity Units1x1 Unity Units
사용 용도복잡한 표면 변형, 지형, 물결 효과간단한 표면, UI, 텍스처 표출
성능비교적 무거움가벼움

사용 사례에 따른 선택

Plane 사용 시:

  • 복잡한 표면 처리:
    • 물결, 굴곡, 지형의 세부 변형이 필요할 때.
  • 세부적인 텍스처 매핑:
    • 여러 UV 좌표가 필요한 경우.

Quad 사용 시:

  • 단순 표면 처리:
    • UI 요소 배치, 단순 텍스처 표시.
  • 성능이 중요한 경우:
    • 많은 개체를 다루는 상황에서 적합.

🏷️ C# : base

base는 부모 클래스의 생성자나 메서드를 호출할 때 사용됩니다.
base(weapon)은 부모 생성자 호출, base.Attack()은 부모 메서드 호출입니다.



📖 대마왕의 유니티 URP 셰이더 그래프


06 트라이플래너(Triplaner)

Triplaner는 UV 좌표 없이 균일한 텍스쳐를 입힐 수 있는 좌표를 만든다.
UV를 펼 시간이 없거나, 동적으로 지형이 변경되는 상황에 사용할 수 있다.

위(Y축)에서 내려다봤을 때 문제없는, 일반적인 UV.
U는 Z축이 되고, V는 X축이 된다.

오른쪽(X축)에서 봤을 때 문제 없는 UV.
U는 Z축이 되고, V는 Y축이 된다.

각 축에 대해 뽑아낸 normal vector를, 각 축에 대한 UV를 입력한 텍스쳐와 곱하고
(Absolute를 해주는 이유는, 반대편에서도 보이게 하기 위함)

전부 더해주면 모든 축의 시선에 대해 UV가 제대로 입혀진다.

물론 내장된 Triplaner 노드도 있긴 하지만,
직접 만들면 옆면에 한해 다른 텍스쳐를 입히기, 일정 각도 이상이 되면 옆면에 다른 텍스쳐 입히기 등 다양한 응용을 할 수 있다.

07 뎁스 페이드 (Depth Fade)

뎁스 페이드

  • 물체에 닿았을 때 부드럽게 사라지는 효과
  • 유니티 파티클 기능에선 소프트 파티클이라고도 한다
  • 물이 육지에 닿는 부분에서 사용할 수 있다
  • 보호막 이펙트에도 사용할 수 있다

  • 렌더링 파이프라인 애셋에서 Detph Texture를 활성화시킨다
  • Scene Depth 노드를 꺼내어 Sampling을 Eye로 설정한다
  • Graph Settings > Surface Type > Transparent로 설정한다
  • Scene Depth 노드를 꺼낸다. Transparent로 설정했었으므로, 이 셰이더가 적용된 물체는 모든 Opaque(불투명)가 그려진 후에 그려진다.
  • Scene Position 노드를 꺼낸다. Raw 모드의 A값(w값)은 해당 오브젝트의 깊이값이다.
  • 두 depth 값을 빼고, Saturate를 이용하여 0에서 1 사이로 잘라준다.
  • 깊이 차가 별로 없는 부분은 0, 깊이 차가 큰 부분은 1에 가까워질텐데, 이것을 one minus 노드로 반전시키고, saturate한 뒤 alpha 값에 넣어준다.

Troubleshooting

  • scene depth와 물체의 depth값의 차이가 책과 다름.
  • 예제 파일을 확인해보니 Blending Mode가 Additive였고, Quad가 아닌 Plane이었음. (Plane이 Quad보다 10배 커서 깊이값이 달랐다)

Part 21 | 물 만들기

물의 특징

  • 바닥이 보이게 투명해야 한다
  • (환경을) 반사해야 한다
  • 빛나야 한다
  • 굴절되어야 한다
  • 흘러가야 한다
  • 육지와 맞닿는 면이 투명하거나 포말이 생겨야 한다

02 기본적인 물 효과 만들기

  • Waterbump 노말 텍스쳐를 Cubemap 텍스쳐의 Normal에 연결시킨다
  • 이 결과를 Base Color에 넣으면 그나마 자연스럽지만, 책에선 Emission에 일단 넣긴 함.

  • Base Color를 검은색으로 바꾼다
  • Normal Strength 노드로 노말맵의 강도를 낮춘다
  • 타일링을 2x2로 증가시킨다
  • Transparent로 바꾼다
  • water에 사용된 plane의 Inspector > Lighting > Cast Shadows > Off

주변과 어느 정도 어울리는 반사로 만들어주기 위해, 주변 환경을 캡처하여 정반사 소스로 만드는 리플렉션 프로브를 사용한다.

  • GameObject > Light > Reflection Probe를 만든 후 씬의 중앙에 놓는다
    • 땅에 묻히거나 오브젝트 안에 위치시키면 안된다. 리플렉션 프로브는 그 위치에서 사방을 촬영하는 카메라라고 생각하면 된다.
  • 리플렉션 프로브를 선택하고, Edit Bounding Volume > 캡쳐가 되길 원하는 배경이 충분히 들어갈만큼 크기를 조절한다
    • 이 영역에 들어가지 않는 오브젝트는 촬영되지 않으며, 빈자리는 스카이 박스로 채워진다.
  • 리플렉션 프로브에 촬영되길 원하는 오브젝트들을 선택하고, Inspector에서 Static 옆 화살표 클릭하고 Reflection Probe Static을 활성화
  • Bake한 후 큐브맵 대신 Reflection Probe를 꺼내어 연결한다.

모든 물체, 특히 비금속 물체는 시야 기준으로 기울어질수록 반사가 심해진다. 물도 마찬가지다.
프레넬로 이를 구현할 수 있다.

  • Fresnel Effect를 꺼내어 Alpha에 넣어주고, Power를 조절한다.
    • 내려다보는 바로 아래 각도는 많이 투명해지고, 시야각과 멀어지는 부분에선 투명도가 거의 없어지고 반사가 심해진다.
  • Fresnel Effect를 Lerp로 반전시킨 후에 Normal Strength에 넣어준다.
    • 노말의 강도가 시야에서 멀어지면 옅어지고 플랫해진다.

제자리에서 찰랑거리는 물은 각기 다른 방향으로 움직이는 normal 텍스쳐를 blend하면 간단하게 구현할 수 있다.

03 물 표면 웨이브 만들기

Sine Wave 만들기

y 위치를 x축 위치에 대응되는 sine값으로 치환하면 이런 모양이 나온다.

파장의 길이와 높이 수치를 조절하고, sine에 대입할 x축 위치에 시간을 더해서 흘러가게 만든다.

  • 아직 노말 벡터는 굴곡대로 계산되고 있지 않고 y축 방향만 가리키고 있었다
  • 미분해서 cosine으로 바뀌고, Y축 높이가 곱해지는 만큼 똑같이 곱해준 뒤, X축 방향에는 원래 탄젠트였던 1을 넣고, noramlize하면 Tangent 벡터가 된다.
  • Z축 벡터인 (0,0,1)와 외적하면 Normal 벡터가 된다.


📖 헤드 퍼스트 디자인 패턴


Chapter 03 | 객체 꾸미기, 데코레이터 패턴

코드는 변경에는 닫혀 있고 확장에는 열려 있어야 한다.
무조건 OCP를 적용하면 시간 낭비가 될 수 있고, 추상화를 하다보면 코드가 복잡해지기 때문에, 가장 가변적인 부분만 OCP를 적용하는게 좋다.

데코레이터 패턴

  • 객체에 추가 요소를 동적으로 더할 수 있다
  • 데코레이터를 사용하면 서브클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있다
  • 데코레이터의 슈퍼클래스는 자신이 장식하고 있는 객체의 슈퍼클래스와 같다
  • 한 객체를 여러 개의 데코레이터로 감쌀 수 있다
  • 데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 일 말고도 추가 작업을 수행할 수 있다

데코레이터는 원 객체를 감싸고 또 감싸는 식으로 작동.
데코레이터 객체에서 특정 기능 구현하는 방식은 자기가 할거 한 후에 변수에 담고 있는 (감싼) 객체의 같은 메소드에 값을 넘겨주는 것.

데코레이터의 단점

  • 자잘한 클래스들이 많이 추가되곤 한다
  • 특정 형식에 의존하는 클라이언트 코드에는 사용할 수 없다
  • 요소를 초기화하는 데 필요한 코드가 복잡해진다
public astract class Beverage {
	string description = "제목 없음";
    
    public string GetDescription() {
    	return description;
    }
    
    public abstract double cost();
|

모든 객체를 위한 슈퍼 클래스 예시.
책에서 설정해놓은 상황은 레거시 코드가 추상 클래스여서 굳이 기존 코드를 바꾸는 건 피해야 하니까 추상 클래스로 만든거라고 하고,
원래는 인터페이스로 구현한다고 함. 인터페이스로 구현하면 GetDescription()도 컴포넌트의 슈퍼 클래스에서 선언하면 되는듯.

public abstract class CondimentDecorator : Beverage {
	protected Beverage beverage;
    public abstract string GetDesription();
}

데코레이터를 나타내는 추상 클래스 예시.
beverage를 담을 변수를 구현한다.

추가 예시

Component: 기본 기능을 정의하는 인터페이스
ConcreteComponent: Component 인터페이스를 구현하며, 기본 기능을 제공하는 클래스
Decorator: Component 인터페이스를 구현하며, 추가 기능을 제공하기 위해 다른 Component 객체를 감싸는 클래스
ConcreteDecorator: Decorator 클래스를 상속받아 추가적인 기능을 실제로 구현하는 클래스

// Component
interface IWeapon {
    string Attack();
}

// ConcreteComponent
class BasicWeapon : IWeapon {
    public virtual string Attack() {
        return "공격한다";
    }
}

// Decorator
abstract class WeaponDecorator : IWeapon {
    protected IWeapon decoratedWeapon;

    public WeaponDecorator(IWeapon weapon) {
        this.decoratedWeapon = weapon;
    }

    public virtual string Attack() {
        return decoratedWeapon.Attack();
    }
}

// ConcreteDecorator
class FireWeaponDecorator : WeaponDecorator {
    public FireWeaponDecorator(IWeapon weapon) : base(weapon) {}

    public override string Attack() {
        return base.Attack() + ", 불꽃 속성 공격을 추가한다";
    }
}

// 사용 예
IWeapon weapon = new BasicWeapon();
weapon = new FireWeaponDecorator(weapon);
Console.WriteLine(weapon.Attack());

https://webcodur.tistory.com/41

0개의 댓글