<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>power_overwhelming</title>
        <link>https://velog.io/</link>
        <description>이것 저것 다해보는 삶</description>
        <lastBuildDate>Thu, 23 Jul 2026 08:18:15 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>power_overwhelming</title>
            <url>https://images.velog.io/images/bak_chun8/profile/fb237a59-9e1d-4b15-8aec-77cf4716f727/8300.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. power_overwhelming. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bak_chun8" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TIL: Unreal C++ CH3 팀프로젝트 70~81일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-7081%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-7081%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 23 Jul 2026 08:18:15 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--886시간-34분-">*<em>누적 학습 시간 : 886시간 34분 *</em></h2>
<h4 id="📅-2026-07-23">📅 2026-07-23</h4>
<p>조 : Team12 RPG
프로젝트 명 : Katana(가제)
목표 : Kannagi Usagi / Sekiro Like와 비슷한 보스전투 구현
기여 파트</p>
<ul>
<li>Enemy</li>
<li>AIController</li>
<li>BB, BT</li>
<li>BTTask</li>
<li>팀 생산성 향상을 위한 UE5 프로젝트 컨벤션 수립 및 Git 워크플로우 최적화</li>
<li>그래픽 리소스 관리 파이프라인 구축(예정)</li>
<li>VFX</li>
</ul>
<h1 id="팀프로젝트-종료">팀프로젝트 종료</h1>
<p>사실 팀플하는동안 TIL 쓸 시간보다는 좀 더 집중하고싶어서 TIL을 따로 작성하지 못했다.</p>
<p>그래서 해당 글에서 정리해서 한번에 작성하려고한다.</p>
<h1 id="vfx">VFX</h1>
<p>주로 2주가량 거의 VFX만 붙잡고 있었는데 주요하게 구현한 VFX는</p>
<ul>
<li><p>가드</p>
</li>
<li><p>패링</p>
</li>
<li><p>혈흔 + decal</p>
</li>
<li><p>보스 공격 시 바닥 깨짐</p>
</li>
<li><p>힐링포션</p>
</li>
<li><p>플레이어 공격 시 소드트레일(미적용)
정도 있는데 스크린샷으로 남겨보려고한다.</p>
</li>
<li><p>CelShading(ToonShader)</p>
</li>
</ul>
<h2 id="가드">가드</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/6b7e7454-3dc9-4d8c-8e2d-742b01904200/image.png" alt=""></p>
<h2 id="패링">패링</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/ebe16253-c69c-4ab7-b880-fb0dbe479329/image.png" alt="">
<img src="https://velog.velcdn.com/images/bak_chun8/post/d1a70d1a-83a5-4da8-9074-76de81f59b8f/image.png" alt="">
<img src="https://velog.velcdn.com/images/bak_chun8/post/22ac426b-4a3c-420e-8178-eb9466bdcbae/image.png" alt=""></p>
<h2 id="혈흔--바닥깨짐">혈흔 + 바닥깨짐</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/6fe42d3c-6879-4175-b17c-56a4009b183f/image.png" alt=""></p>
<h2 id="힐링포션">힐링포션</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/e36aa5d9-614c-47e7-b0fa-ff321f970b3f/image.png" alt=""></p>
<h2 id="toonshader">ToonShader</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/297f918d-0aba-41a4-936c-be73cc5320c9/image.png" alt="">
잘 보면 캐릭터 외곽선 + 머터리얼이 점토같은 부분이 있다.</p>
<h1 id="회고">회고</h1>
<h2 id="좋았던-점">좋았던 점</h2>
<ul>
<li>잘하는 분들과 프로젝트를 진행할 수 있어서 편했고 배울 점이 많았습니다.</li>
<li>빠르게 구현하기 어려울 것이라고 생각했던 프로젝트를 생각 외로 매우 빨리 구현하시는 모습들을 보면서
자극을 많이 받았습니다.</li>
<li>중간에 한 명이 줄어서 빠듯할 것이라고 예상했으나 빠진 한명 분의 몫만큼 다들 열심히 하셔서 무사히 잘 마무리 할 수 있었습니다.</li>
<li>이전에는 스페셜리스트가 모인 집단이 더 좋을 수 있겠다고 생각하고 있었는데 팀 내에 제네럴리스트가 한명은 꼭 있어야겠구나 하는 확신을 가지게 되어 기쁩니다.</li>
</ul>
<h2 id="개선할-점">개선할 점</h2>
<ul>
<li>개발을 맡게된 부분이 상상했던 것과는 다르다보니 작업효율이 떨어지는 문제가 있었습니다.</li>
<li>관심있는 부분의 정의를 더 포괄적으로 생각해야겠다 마음먹었습니다. 1과 동일하게 관심있는 부분이 아니면 작업효율이 떨어졌습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ CH3 팀프로젝트 69일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-69%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-69%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 09 Jul 2026 02:46:44 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--754시간-34분-">*<em>누적 학습 시간 : 754시간 34분 *</em></h2>
<h4 id="📅-2026-07-08">📅 2026-07-08</h4>
<p>조 : Team12 RPG
프로젝트 명 : Katana(가제)
목표 : Kannagi Usagi / Sekiro Like와 비슷한 보스전투 구현
기여 파트</p>
<ul>
<li>Enemy</li>
<li>AIController</li>
<li>BB, BT</li>
<li>BTTask</li>
<li>팀 생산성 향상을 위한 UE5 프로젝트 컨벤션 수립 및 Git 워크플로우 최적화</li>
<li>그래픽 리소스 관리 파이프라인 구축(예정)</li>
<li>VFX</li>
</ul>
<h1 id="ta-스터디">TA 스터디</h1>
<p><a href="https://velog.io/@bak_chun8/TA-Radial-Ring">30일간 하루에 Material 하나씩 만들기 챌린지</a></p>
<h1 id="팀프로젝트">팀프로젝트</h1>
<p>오늘부터는 내가 VFX를 담당하기로 했다. 아무래도 이쪽으로 관심이 많아서</p>
<p>Ribbon으로 trail 하니까
검 궤적이랑 다르게 line이 나가는 경우가 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TA: Flowing Water]]></title>
            <link>https://velog.io/@bak_chun8/TA-Flowing-Water</link>
            <guid>https://velog.io/@bak_chun8/TA-Flowing-Water</guid>
            <pubDate>Thu, 09 Jul 2026 02:46:24 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-07-09">📅 2026-07-09</h4>
<p>저는 오늘부터 30일간 매일 1개씩 Material을 만들어볼겁니다!(유튜브 톤으로)</p>
<h1 id="주제">주제</h1>
<p>Flowing Water</p>
<p>오늘부터는 움직이는 Material들을 만들어볼것이다.</p>
<p>흐르는 물을 만들었다</p>
<h1 id="사용-노드">사용 노드</h1>
<h2 id="panner">Panner</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/3394fbaa-4e6b-4112-88c0-c2888acfc5c0/image.png" alt=""></p>
<p><code>Panner</code>은 UV좌표를 시간에 따라 이동시키는 노드이다.</p>
<p>HLSL 수식은</p>
<pre><code class="language-cpp">float2 pannedUV = UV + Speed * Time; </code></pre>
<p>Panner은 매시의 표면이 실제로 움직이는 것이 아니라 UV좌표가 변경되는 것이기 때문에 오해하면 안된다.</p>
<h2 id="blendanglecorrctednormals">BlendAngleCorrctedNormals</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/fb5a8e12-bc74-4e27-8d36-8accf1a8d104/image.png" alt=""></p>
<p>NormalMap 두개를 자연스럽게 합성하는 MF이다.
Normal은 단순한 색이 아니라 방향벡터이기 때문에
RGB를 Add 노드로 더해버리면 방향정보가 틀어질 수 있다.</p>
<p>단점은 단순히 합성하는 역할을 하기때문에 <code>FlattenNormal</code> 노드를 함께 사용해 Flatness를 조절해줄 필요가 있다.</p>
<h2 id="flattennormal">FlattenNormal</h2>
<ul>
<li>Flatness 낮음 = 원본 Normal에 가까움</li>
<li>Flatness 높음 = 더 평평한 Normal</li>
</ul>
<h3 id="flatness-05">Flatness 0.5</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/de897e33-892c-42fb-9b3f-4aeb4e657bc2/image.png" alt=""></p>
<h3 id="flatness-1">Flatness 1</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/3ddb2faa-1e65-4bbc-9b5a-f854ade671b0/image.png" alt=""></p>
<h1 id="결과">결과</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/1b8c4b33-0712-4cc2-be08-366a187f5737/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/afdb1bf1-b04d-403f-b35f-b3a6b2608e8f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/7fb6d69d-c498-4645-a672-b0910e2cb1ff/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ CH3 팀프로젝트 68일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-68%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-68%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 08 Jul 2026 01:56:42 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--742시간-34분-">*<em>누적 학습 시간 : 742시간 34분 *</em></h2>
<h4 id="📅-2026-07-07">📅 2026-07-07</h4>
<p>조 : Team12 RPG
프로젝트 명 : Katana(가제)
목표 : Kannagi Usagi / Sekiro Like와 비슷한 보스전투 구현
기여 파트</p>
<ul>
<li>Enemy</li>
<li>AIController</li>
<li>BB, BT</li>
<li>BTTask</li>
<li>팀 생산성 향상을 위한 UE5 프로젝트 컨벤션 수립 및 Git 워크플로우 최적화</li>
<li>그래픽 리소스 관리 파이프라인 구축(예정)</li>
</ul>
<h1 id="ta-스터디">TA 스터디</h1>
<p><a href="https://velog.io/@bak_chun8/TA-Grid">30일간 하루에 Material 하나씩 만들기 챌린지</a></p>
<h1 id="팀프로젝트">팀프로젝트</h1>
<p>공격 Montage 추가 및 적용
Enemy 이동 BTTask 로직 수정
BTTask_ExecuteAttack 에러 수정
Enemy 이동 시 진행방향 매시 Rotation 수정</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TA: Radial Ring]]></title>
            <link>https://velog.io/@bak_chun8/TA-Radial-Ring</link>
            <guid>https://velog.io/@bak_chun8/TA-Radial-Ring</guid>
            <pubDate>Wed, 08 Jul 2026 01:54:34 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-07-08">📅 2026-07-08</h4>
<p>저는 오늘부터 30일간 매일 1개씩 Material을 만들어볼겁니다!(유튜브 톤으로)</p>
<h1 id="주제">주제</h1>
<p>Radial Ring이 뭘까?</p>
<ul>
<li>Radial Gradient = 중앙이 밝고 바깥으로 갈수록 어두워짐</li>
<li>Radial Ring = 중앙은 비었고 반지름 위치만 밝음</li>
</ul>
<h1 id="radial">Radial</h1>
<p><code>Radial</code>은 중심에서 밖으로 퍼지는 방향이라는 뜻이다.</p>
<p>Material에서는</p>
<ul>
<li>중심점 = UV (0.5, 0.5)</li>
<li>현재 픽셀 위치 = UV (U, V)</li>
<li>두 점 사이의 거리 = Distance</li>
</ul>
<p>Radial Material은 중심으로부터의 거리값을 이용한다.</p>
<p>그래서 <code>Radius = 0.35</code>인 Radial Ring을 만들면</p>
<ul>
<li>중심 거리 0.00 = 어두움</li>
<li>중심 거리 0.20 = 어두움</li>
<li>중심 거리 0.35 = 밝음</li>
<li>중심 거리 0.50 = 어두움</li>
</ul>
<h1 id="ring">Ring</h1>
<p>일단 Ring에는 2개의 개념이 필요한데</p>
<ol>
<li>Radius = 고리가 생길 위치</li>
<li>Width = 고리의 두께</li>
</ol>
<p>핵심 수식은</p>
<pre><code class="language-cpp">float dist = distance(UV, Center);
float ring = 1 - saturate(abs(dist - Radius) / width);</code></pre>
<p><code>distance(UV, Center)</code></p>
<ul>
<li>중심에서 현재 픽셀까지의 거리</li>
</ul>
<p><code>dist - Radius</code></p>
<ul>
<li>내가 원하는 링 위치에서 얼마나 벗어났는가</li>
</ul>
<p><code>abs(dist - Radius)</code></p>
<ul>
<li>안/밖 상관없이 링 위치와의 거리 차이</li>
</ul>
<p><code>/ width</code></p>
<ul>
<li>링 두께 조절</li>
</ul>
<p><code>saturate( ... )</code></p>
<ul>
<li>링 중심선에 가까울수록 밝게, 멀수록 어둡게</li>
</ul>
<h1 id="결과">결과</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/7772f108-78fe-46bd-9afe-0a61634a8948/image.png" alt="">
<img src="https://velog.velcdn.com/images/bak_chun8/post/863ef5bd-a4cf-4086-8822-761b9d40d91e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TA: Grid Pattern]]></title>
            <link>https://velog.io/@bak_chun8/TA-Grid</link>
            <guid>https://velog.io/@bak_chun8/TA-Grid</guid>
            <pubDate>Tue, 07 Jul 2026 02:19:41 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-07-07">📅 2026-07-07</h4>
<p>저는 오늘부터 30일간 매일 1개씩 Material을 만들어볼겁니다!(유튜브 톤으로)</p>
<h1 id="주제">주제</h1>
<p>Stripe를 만들어봤으니 Stripe 두개를 합쳐서 Grid를 만들어보자</p>
<h1 id="u-v-노드">U, V 노드</h1>
<h2 id="u">U</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/45bcd9a9-b57c-4866-992e-ae47c6dc7d3f/image.png" alt=""></p>
<h2 id="v">V</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/f8c144ce-487a-44ae-afed-8274a0c9ebd5/image.png" alt=""></p>
<h1 id="연산에-따른-값-변화">연산에 따른 값 변화</h1>
<p>마지막 U와 V의 interpolate값을 Add 혹은 Multiply한 값을 BaseColor에 넣어줘야하는데</p>
<h2 id="add">Add</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/cf28d2c3-316d-4243-bc22-a75a507ce577/image.png" alt=""></p>
<h2 id="multiply">Multiply</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/97e9b59a-42b0-4e95-8b02-db719191aa74/image.png" alt=""></p>
<p>Add는 두 스트라이프를 합치는 용도고
Multiply는 겹치는 부분만 1로 만드는 용도라고 보면 된다.</p>
<p>그래서 이 상황에서는 Add+Saturate 쓰는게 좋다.</p>
<h1 id="결과">결과</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/86363304-59a8-4a6b-8ad2-7edc20e3c80f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ CH3 팀프로젝트 67일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-67%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-67%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 07 Jul 2026 01:36:38 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--730시간-34분-">*<em>누적 학습 시간 : 730시간 34분 *</em></h2>
<h4 id="📅-2026-07-06">📅 2026-07-06</h4>
<p>조 : Team12 RPG
프로젝트 명 : Katana(가제)
목표 : Kannagi Usagi / Sekiro Like와 비슷한 보스전투 구현
기여 파트</p>
<ul>
<li>Enemy</li>
<li>AIController</li>
<li>BB, BT</li>
<li>BTTask</li>
<li>팀 생산성 향상을 위한 UE5 프로젝트 컨벤션 수립 및 Git 워크플로우 최적화</li>
<li>그래픽 리소스 관리 파이프라인 구축(예정)</li>
</ul>
<h1 id="ta-스터디">TA 스터디</h1>
<p><a href="https://velog.io/@bak_chun8/TA-Procedural-Checker">30일간 하루에 Material 하나씩 만들기 챌린지</a>
<a href="https://velog.io/@bak_chun8/TA-Procedural-Stripe">주말에 두개함</a></p>
<h1 id="팀프로젝트">팀프로젝트</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[TA: Procedural Stripe]]></title>
            <link>https://velog.io/@bak_chun8/TA-Procedural-Stripe</link>
            <guid>https://velog.io/@bak_chun8/TA-Procedural-Stripe</guid>
            <pubDate>Mon, 06 Jul 2026 01:06:50 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-07-06">📅 2026-07-06</h4>
<p>저는 오늘부터 30일간 매일 1개씩 Material을 만들어볼겁니다!(유튜브 톤으로)</p>
<h1 id="주제">주제</h1>
<p>수식으로 stripe 만들어보기</p>
<p>막상 해보니까 어렵지는 않았고 checker와 아주 조금씩 다른 부분들이 있었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/0e394e39-323e-46a9-af14-17f563142316/image.png" alt=""></p>
<p>Checker와 마찬가지로 TextureCoordinate 노드를 사용해서 위치를 정하는데
조금 다른 부분이 Floor,Fmod대신 Frac과 Step을 쓴다</p>
<p>이전 글에서 Floor를 사용해서 구역을 나눈다고 말했는데</p>
<ul>
<li>0.2 -&gt; 0번 칸</li>
<li>0.8 -&gt; 0번 칸</li>
<li>1.1 -&gt; 1번 칸</li>
<li>1.9 -&gt; 1번 칸</li>
<li>2.4 -&gt; 2번 칸</li>
</ul>
<p>Frac을 쓰게되면 반대로 소수점아래만 남기고 정수부분을 제거한다.</p>
<ul>
<li>0.2 -&gt; 0.2</li>
<li>0.8 -&gt; 0.8</li>
<li>1.1 -&gt; 0.1</li>
<li>1.9 -&gt; 0.9</li>
<li>2.4 -&gt; 0.4</li>
</ul>
<p>Step은 여기서 Stripe의 weight지정하는 역할로 사용됐는데 보기 쉽게 StripeWidth라고 표현했다.</p>
<p><code>Step(Repeated, StripeWidth)</code>
<code>StripeWidth = 0.2</code></p>
<ul>
<li>Repeated &lt; 0.2 -&gt; 0</li>
<li>Repeated &gt;= 0.2 -&gt; 1</li>
</ul>
<p>Frac된 최종 값이 StripeWidth값보다 크면 1 작으면 0이 된다.</p>
<p>결과적으로는 BaseColorA의 영역이 짧아지고 BaseColorB의 영역이 커진다.</p>
<h2 id="결과">결과</h2>
<h3 id="stripewidth--02">StripeWidth = 0.2</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/c634ce62-c13e-4677-a3d6-9f26cb8b607d/image.png" alt=""></p>
<h3 id="stripewidth--05">StripeWidth = 0.5</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/a0830f07-122f-4fcf-9682-2f171607f16e/image.png" alt=""></p>
<h3 id="stripewidth--08">StripeWidth = 0.8</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/0302e12a-dad6-4160-b1da-2db0d436f610/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TA: Procedural Checker]]></title>
            <link>https://velog.io/@bak_chun8/TA-Procedural-Checker</link>
            <guid>https://velog.io/@bak_chun8/TA-Procedural-Checker</guid>
            <pubDate>Sun, 05 Jul 2026 05:38:10 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-07-05">📅 2026-07-05</h4>
<p>저는 오늘부터 30일간 매일 1개씩 Material을 만들어볼겁니다!(유튜브 톤으로)</p>
<h1 id="주제">주제</h1>
<p>오늘의 주제는</p>
<blockquote>
<p>텍스쳐 없이 UV 수식만으로 체커보드 패턴 만들기</p>
</blockquote>
<p>Procedural Checker의 핵심은 UV 좌표를 이용해서 체커보드의 칸을 수학적으로 나누는 것인데...</p>
<p>UV가 뭐고 체커보드가 뭔데? 먼저 알아보고 가자</p>
<h1 id="uv란">UV란?</h1>
<p>UV는 3D 모델 표면 위의 좌표를 2D 좌표로 표현한 것이다.
3D 모델은 실제로 입체로 표현되는데 해당 모델에 입혀지는 텍스쳐이미지는 2D다.</p>
<ul>
<li>U : 가로방향 좌표</li>
<li>V : 세로방향 좌표</li>
</ul>
<p>Unreal에서 TextureCoordinate 노드는 UV텍스쳐 좌표를 2채널 벡터값으로 출력하고 Material에서 UV채널 선택, 타일링지정, 매시UV 조작에 사용할 수 있다.</p>
<h2 id="이해하기-쉬운-예시">이해하기 쉬운 예시</h2>
<p>종이 박스를 생각하면 되는데</p>
<ul>
<li>박스 = 큐브 메시</li>
<li>전개도 = UV</li>
<li>전개도 위에 그린 그림 = 텍스쳐</li>
<li>다시 접으면? &gt; 모델에 텍스쳐가 입혀짐</li>
</ul>
<h2 id="uv는-보통-01-공간">UV는 보통 0~1 공간</h2>
<pre><code class="language-text">V 1.0  ┌──────────────┐
.
.
.
.
.
V 0.0  └──────────────┘
      U 0.0  . . .  1.0</code></pre>
<h2 id="uv에-숫자를-곱하면">UV에 숫자를 곱하면?</h2>
<p>UV = 0 ~ 1
UV * 4 = 0 ~ 4</p>
<p>UV를 4칸으로 쪼갤 준비가 됐다.</p>
<ul>
<li>0 ~ 1</li>
<li>1 ~ 2</li>
<li>2 ~ 3</li>
<li>3 ~ 4</li>
</ul>
<h1 id="체커보드란">체커보드란?</h1>
<p>체커보드는 말 그대로 두 색이 번갈아 배치된 격자 패턴.</p>
<p>대표적으로 체스판/바둑판 비슷한 패턴을 생각하면 된다.</p>
<p>■ □ ■ □
□ ■ □ ■
■ □ ■ □
□ ■ □ ■</p>
<p>오늘은 이걸 이미지가 아니라 수식으로 제어해볼 것이다.</p>
<h2 id="체커보드-수식만들기">체커보드 수식만들기</h2>
<p>핵심은 각 픽셀이 몇 번째 칸에 있는지 알아내는 것이다.</p>
<p>예를들어 UV에 <code>TileCount = 4</code> 를 곱하면</p>
<ul>
<li>U : 0 ~ 4</li>
<li>V : 0 ~ 4</li>
</ul>
<p>Floor를 사용해 소수점을 버리면 칸 번호가 나온다.</p>
<ul>
<li>0.2 -&gt; 0번 칸</li>
<li>0.8 -&gt; 0번 칸</li>
<li>1.1 -&gt; 1번 칸</li>
<li>1.9 -&gt; 1번 칸</li>
<li>2.4 -&gt; 2번 칸</li>
</ul>
<blockquote>
<p>floor(U * TileCount) = U 방향 칸 번호
floor(V * TileCount) = V 방향 칸 번호</p>
</blockquote>
<p>이후 두 칸 번호를 더하고 2로 나눈 나머지를 모으면 그게 체커보드가 된다.</p>
<h1 id="결과">결과</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/89418782-5cbc-48d2-b4b4-3f708e8275a0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/a10af5c9-c5ad-4a7a-8620-e5d529f68743/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/21c41f2b-ece4-41e0-bec2-43e38175d765/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TA: Metallic]]></title>
            <link>https://velog.io/@bak_chun8/TA-Metallic</link>
            <guid>https://velog.io/@bak_chun8/TA-Metallic</guid>
            <pubDate>Sat, 04 Jul 2026 09:23:17 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-07-04">📅 2026-07-04</h4>
<p>저는 오늘부터 30일간 매일 1개씩 Material을 만들어볼겁니다!(유튜브 톤으로)</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/524d2db0-6173-4722-ab48-21a0c9115475/image.png" alt=""></p>
<p>순서대로
non-metal-red, metal-red, metal-gold, metal-chrome, non-metal-black으로 만들었다.</p>
<p>색상수치는 제외하고 이전에 학습한 Roughness, Specular, Metallic 3개를 조정해서 만들었다.</p>
<h2 id="핵심-관찰">핵심 관찰</h2>
<p>Metallic 0에서는 BaseColor가 표면의 고유색처럼 보인다.
Metallic 1에서는 BaseColor가 금속 반사색처럼 작동한다.
Roughness가 낮으면 금속과 비금속 모두 반짝일 수 있다.
따라서 반짝인다고 무조건 금속은 아니다.</p>
<h2 id="실수-방지">실수 방지</h2>
<p>순수 재질에서는 Metallic을 보통 0 또는 1로 둔다.
0.3, 0.5 같은 중간 Metallic 값은 금속/비금속이 섞인 영역을 표현할 때 사용한다.</p>
<h2 id="hlsl-변환-후보">HLSL 변환 후보</h2>
<p>없음.
오늘은 PBR Metallic Workflow 이해가 목적이다.</p>
<h2 id="metallic--0">Metallic = 0</h2>
<ul>
<li>플라스틱</li>
<li>고무</li>
<li>나무</li>
<li>돌</li>
<li>천</li>
<li>피부</li>
<li>페인트</li>
<li>도자기</li>
</ul>
<p>비금속은 표면 자체의 색, 즉 Base Color가 확산색처럼 보인다.</p>
<h2 id="metallic--1">Metallic = 1</h2>
<ul>
<li>금</li>
<li>은</li>
<li>철</li>
<li>구리</li>
<li>알루미늄</li>
<li>크롬</li>
</ul>
<h2 id="metallic-중간값">Metallic 중간값</h2>
<ul>
<li>녹슨 금속</li>
<li>먼지 묻은 금속</li>
<li>페인트가 벗겨진 금속</li>
<li>흙/기름/오염이 섞인 금속</li>
</ul>
<p>중간값 자체가 무조건 틀린 건 아니다.
다만 오늘의 순수 재질 비교에서는 중간값을 정답처럼 쓰면 안 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ CH3 팀프로젝트 65일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-65%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-65%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 03 Jul 2026 11:49:11 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--706시간-34분-">*<em>누적 학습 시간 : 706시간 34분 *</em></h2>
<h4 id="📅-2026-07-03">📅 2026-07-03</h4>
<p>조 : Team12 RPG
프로젝트 명 : Katana(가제)
목표 : Kannagi Usagi / Sekiro Like와 비슷한 보스전투 구현
기여 파트</p>
<ul>
<li>Enemy</li>
<li>AIController</li>
<li>BB, BT</li>
<li>BTTask</li>
<li>팀 생산성 향상을 위한 UE5 프로젝트 컨벤션 수립 및 Git 워크플로우 최적화</li>
<li>그래픽 리소스 관리 파이프라인 구축(예정)</li>
</ul>
<h1 id="ta-스터디">TA 스터디</h1>
<p><a href="https://velog.io/@bak_chun8/TA-Glossy-Rough">30일간 하루에 Material 하나씩 만들기 챌린지</a></p>
<h1 id="팀프로젝트">팀프로젝트</h1>
<h2 id="enemy-ai-전투-흐름-개선">Enemy AI 전투 흐름 개선</h2>
<p>오늘은 Enemy의 Behavior Tree 구조를 세키로/소울라이크 보스전에 가깝게 수정했다.</p>
<p>기존 구조는 <code>SelectNextAction</code>에서 이동, 일반공격, 강공격을 랜덤으로 고르는 방식이었다. 하지만 실제 소울라이크 보스는 플레이어에게 계속 직선으로 접근하기보다는, 일정 시간 플레이어를 바라보며 좌우로 간을 보다가 공격 타이밍에 돌진하거나 특수 공격을 사용한다.</p>
<p>그래서 전투 흐름을 다음처럼 변경했다.</p>
<pre><code class="language-text">Strafe Around Target
-&gt; Select Next Action
-&gt; 공격 분기</code></pre>
<h3 id="strafe-around-target">Strafe Around Target</h3>
<p>새로운 <code>BTTask_StrafeAroundTarget</code>를 추가했다.</p>
<p>이 Task는 일정 시간 동안 Enemy가 플레이어를 바라본 상태로 좌우 스트레이프 이동을 하게 만든다. 스트레이프 중에는 이동 속도를 낮추고, Task가 끝나면 다시 돌진용 속도로 복구한다.</p>
<pre><code class="language-text">StrafeMoveSpeed = 150~300
AfterStrafeMoveSpeed = 600
StrafeDuration = 2~5초</code></pre>
<p>이를 통해 Enemy가 공격 전 바로 접근하지 않고, 플레이어 주변을 돌며 간을 보는 움직임을 만들 수 있게 됐다.</p>
<h3 id="공격-선택-로직-변경">공격 선택 로직 변경</h3>
<p><code>BTTask_SelectNextAction</code>의 역할도 변경했다.</p>
<p>기존에는 이동, 일반공격, 강공격을 모두 선택했지만, 이제 이동은 전투 루프에 고정하고 <code>SelectNextAction</code>은 공격 패턴만 선택한다.</p>
<p>현재 공격은 총 4종이다.</p>
<pre><code class="language-text">SelectedAction = 1, SelectedPattern = 0 -&gt; 일반공격 0
SelectedAction = 1, SelectedPattern = 1 -&gt; 일반공격 1
SelectedAction = 2, SelectedPattern = 0 -&gt; 근접 강공격
SelectedAction = 2, SelectedPattern = 1 -&gt; 원거리 특수 강공격</code></pre>
<p>일정 거리 이상 떨어져 있으면 원거리 특수 강공격을 선택하고, 그 외에는 일반공격 2종과 근접 강공격 1종 중 가중치 랜덤으로 선택한다.</p>
<pre><code class="language-cpp">if (DistanceToTarget &gt;= FarSpecialAttackDistance)
{
    SelectedAction = 2;
    SelectedPattern = 1;
}
else
{
    // Normal 0, Normal 1, Strong 0 중 랜덤 선택
}</code></pre>
<h3 id="최종-behavior-tree-구조">최종 Behavior Tree 구조</h3>
<p>최종 BT 구조는 다음과 같다.</p>
<pre><code class="language-text">ROOT
└─ Main Sequence
   ├─ Service: Update Target Info
   │
   ├─ Strafe Around Target
   ├─ Select Next Action
   │
   └─ Selector_Attack
      │
      ├─ FarAttack
      │  ├─ Decorator: SelectedAction == 2
      │  ├─ Decorator: SelectedPattern == 1
      │  ├─ Rotate To Target
      │  ├─ Execute Attack
      │  └─ Wait
      │
      └─ ApproachAttack
         ├─ Set Movement Speed: 600
         ├─ Move To: TargetActor
         ├─ Rotate To Target
         │
         └─ AttackSelector
            │
            ├─ NormalAttack
            │  ├─ Decorator: SelectedAction == 1
            │  ├─ Execute Attack
            │  └─ Wait
            │
            └─ StrongAttack
               ├─ Decorator: SelectedAction == 2
               ├─ Decorator: SelectedPattern == 0
               ├─ Execute Attack
               └─ Wait</code></pre>
<p>이 구조에서 원거리 특수 강공격은 접근하지 않고 바로 실행된다.
반면 일반공격과 근접 강공격은 먼저 <code>Move To TargetActor</code>로 플레이어에게 돌진한 뒤 실행된다.</p>
<h3 id="거리-디버깅">거리 디버깅</h3>
<p>거리 기반 공격 선택이 제대로 동작하는지 확인하기 위해 <code>EnemyCharacterBase::Tick()</code>에서 Enemy와 Player 사이의 거리를 화면에 출력하도록 했다.</p>
<pre><code class="language-cpp">const float DistanceToPlayer = FVector::Dist(GetActorLocation(), PlayerPawn-&gt;GetActorLocation());

GEngine-&gt;AddOnScreenDebugMessage(
    1,
    0.f,
    FColor::Green,
    FString::Printf(TEXT(&quot;Enemy Distance To Player: %.2f&quot;), DistanceToPlayer)
);</code></pre>
<p>이를 통해 PIE 중 실시간으로 거리를 확인하면서 <code>FarSpecialAttackDistance</code> 값이 적절한지 조정할 수 있게 됐다.</p>
<h3 id="정리">정리</h3>
<p>오늘 작업으로 Enemy AI의 전투 흐름이 단순 랜덤 행동에서 벗어나, 다음과 같은 형태로 바뀌었다.</p>
<pre><code class="language-text">플레이어 락온
-&gt; 좌우 스트레이프
-&gt; 거리 기반 공격 선택
-&gt; 원거리 특수공격 또는 돌진 공격
-&gt; 다시 스트레이프</code></pre>
<p>아직 실제 공격 Montage와 판정은 연결되지 않았지만, 보스 AI의 기본 전투 리듬은 어느 정도 잡힌 상태다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TA: Glossy / Rough]]></title>
            <link>https://velog.io/@bak_chun8/TA-Glossy-Rough</link>
            <guid>https://velog.io/@bak_chun8/TA-Glossy-Rough</guid>
            <pubDate>Fri, 03 Jul 2026 00:34:01 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-07-03">📅 2026-07-03</h4>
<p>저는 오늘부터 30일간 매일 1개씩 Material을 만들어볼겁니다!(유튜브 톤으로)</p>
<h1 id="주제">주제</h1>
<p>오늘은 Roughness 값에 따른 반사 변화 관찰이다.</p>
<p>day1에서 만든 재질에서 Roughness를 바꿧을때 하이라이트와 반사가 어떻게 되는지 알아볼 예정</p>
<h2 id="roughness">Roughness</h2>
<p>Roughness는 언리얼에서 표면이 얼마나 거친지/매끄러운지를 제어한다.
값이 낮으면 거울처럼 선명한 반사
값이 높으면 반사가 퍼져서 무광이된다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/5441e6b3-e061-48f3-9c39-7d76986091a5/image.png" alt=""></p>
<p>순서대로 0.025, 0.2, 0.5, 0.75 이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ CH3 팀프로젝트 64일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-64%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-CH3-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-64%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 02 Jul 2026 16:30:05 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--694시간-34분-">*<em>누적 학습 시간 : 694시간 34분 *</em></h2>
<h4 id="📅-2026-07-02">📅 2026-07-02</h4>
<p>조 : Team12 RPG
프로젝트 명 : Katana(가제)
목표 : Kannagi Usagi / Sekiro Like와 비슷한 보스전투 구현
기여 파트</p>
<ul>
<li>Enemy</li>
<li>AIController</li>
<li>BB, BT</li>
<li>BTTask</li>
<li>팀 생산성 향상을 위한 UE5 프로젝트 컨벤션 수립 및 Git 워크플로우 최적화</li>
<li>그래픽 리소스 관리 파이프라인 구축(예정)</li>
</ul>
<h1 id="ta-스터디">TA 스터디</h1>
<p><a href="https://velog.io/@bak_chun8/TA-MatMattePlastic001">30일간 하루에 Material 하나씩 만들기 챌린지</a></p>
<h1 id="ue5-보스-enemy-ai-기본-구조-구현">UE5 보스 Enemy AI 기본 구조 구현</h1>
<p>오늘은 1:1 보스 전투를 위한 Enemy AI 기본 구조를 잡았다.<br>프로젝트에서는 일반 Enemy를 따로 두지 않고, <code>Enemy = Boss</code> 개념으로 설계했다.</p>
<h2 id="구현한-구조">구현한 구조</h2>
<p>현재 Enemy AI는 다음 흐름으로 동작한다.</p>
<pre><code class="language-text">EnemyCharacterBase
└── EnemyAIController
    └── BehaviorTree / Blackboard
        ├── SelectNextAction
        ├── RotateToTarget
        └── ExecuteAttack</code></pre>
<p><code>EnemyCharacterBase</code>는 <code>BehaviorTreeAsset</code>과 <code>EnemyAttackComponent</code>를 가지고, <code>EnemyAIController</code>는 Possess 시 해당 BT를 실행한다.</p>
<h2 id="blackboard--behavior-tree">Blackboard / Behavior Tree</h2>
<p><code>Blackboard</code>에는 기본적으로 다음 Key를 사용했다.</p>
<pre><code class="language-text">TargetActor
HomeLocation
SelectedAction
SelectedPattern</code></pre>
<p><code>Behavior Tree</code>는 확률로 다음 행동을 선택하고, 선택된 값에 따라 분기하도록 구성했다.</p>
<pre><code class="language-text">SelectedAction == 0 → 이동
SelectedAction == 1 → 일반공격
SelectedAction == 2 → 강공격</code></pre>
<p>BT 구조는 대략 다음과 같다.</p>
<pre><code class="language-text">Root
└── Sequence
    ├── Select Next Action
    └── Selector
        ├── Move
        ├── Normal Attack
        └── Strong Attack</code></pre>
<h2 id="targetactor-세팅">TargetActor 세팅</h2>
<p>초기에는 <code>OnPossess()</code> 시점에 Player를 바로 가져오려 했지만, Player Pawn이 아직 준비되지 않아 <code>TargetActor</code>가 null이 되는 문제가 있었다.</p>
<p>그래서 <code>EnemyAIController</code>에서 일정 간격으로 Player를 다시 찾고, 성공하면 Blackboard의 <code>TargetActor</code>에 세팅하도록 수정했다.</p>
<pre><code class="language-cpp">UpdateTargetActor();</code></pre>
<p>이후 <code>RotateToTarget</code> Task에서 정상적으로 Player를 바라보는 것까지 확인했다.</p>
<h2 id="커스텀-bttask">커스텀 BTTask</h2>
<p>오늘 만든 주요 BTTask는 다음과 같다.</p>
<pre><code class="language-text">BTTask_SelectNextAction
BTTask_RotateToTarget
BTTask_ExecuteAttack</code></pre>
<p><code>SelectNextAction</code>은 확률 기반으로 다음 행동을 선택한다.</p>
<pre><code class="language-text">Move
NormalAttack
StrongAttack</code></pre>
<p><code>RotateToTarget</code>은 Blackboard의 <code>TargetActor</code>를 읽어 Enemy가 대상을 바라보게 한다.</p>
<p><code>ExecuteAttack</code>은 <code>EnemyAttackComponent</code>의 공격 실행 함수를 호출한다.</p>
<h2 id="공격-패턴-구조">공격 패턴 구조</h2>
<p>공격은 <code>EnemyAttackComponent</code>에서 처리하도록 분리했다.</p>
<p>현재는 실제 애니메이션이 없기 때문에 <code>SelectedAction</code>, <code>SelectedPattern</code>을 기준으로 일반공격/강공격 패턴을 구분하는 단계까지 진행했다.</p>
<pre><code class="language-cpp">SelectedAction == 1 → 일반공격
SelectedAction == 2 → 강공격
SelectedPattern == 0 or 1</code></pre>
<p>즉 최종적으로는 다음 네 가지 공격 패턴으로 확장할 수 있다.</p>
<pre><code class="language-text">일반공격 1
일반공격 2
강공격 1
강공격 2</code></pre>
<h2 id="애니메이션-대기-구조">애니메이션 대기 구조</h2>
<p>공격 애니메이션이 붙으면 <code>BTTask_ExecuteAttack</code>은 바로 <code>Succeeded</code>를 반환하면 안 된다.</p>
<p>공격 시작 시에는:</p>
<pre><code class="language-text">EBTNodeResult::InProgress</code></pre>
<p>를 반환하고, 몽타주가 끝났을 때:</p>
<pre><code class="language-cpp">OnAttackFinished.Broadcast();</code></pre>
<p>를 호출해서 Task를 완료시키는 구조로 갈 예정이다.</p>
<p>즉 최종 흐름은 다음과 같다.</p>
<pre><code class="language-text">BTTask_ExecuteAttack
→ EnemyAttackComponent::ExecuteAttack()
→ Attack Montage 재생
→ Montage End
→ OnAttackFinished.Broadcast()
→ FinishLatentTask()
→ 다음 BT 노드 진행</code></pre>
<h2 id="오늘-진행-결과">오늘 진행 결과</h2>
<p>오늘은 실제 공격 애니메이션이나 데미지 판정보다는, 보스 AI가 동작하기 위한 기본 파이프라인을 구성했다.</p>
<p>확인된 내용:</p>
<pre><code class="language-text">Enemy Possess 정상 동작
Behavior Tree 실행 정상
TargetActor 세팅 정상
확률 기반 행동 선택 정상
RotateToTarget 정상
ExecuteAttack 진입 정상
일반공격 / 강공격 패턴 분기 준비 완료</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TA: Mat_Matte_Plastic_001]]></title>
            <link>https://velog.io/@bak_chun8/TA-MatMattePlastic001</link>
            <guid>https://velog.io/@bak_chun8/TA-MatMattePlastic001</guid>
            <pubDate>Thu, 02 Jul 2026 02:59:38 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-07-02">📅 2026-07-02</h4>
<p>저는 오늘부터 30일간 매일 1개씩 Material을 만들어볼겁니다!(유튜브 톤으로)
하루라도 만들지 않는다면 뭔가 드릴 수 있는 건 없습니다..</p>
<h1 id="matte-plastic">Matte Plastic</h1>
<p>예쁜 플라스틱이 목표가 아니라 PBR 기본 입력값을 정확히 이해하는 것을 목표로 만든다.</p>
<h1 id="parameter">Parameter</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/c39b5bd4-6d14-4cda-a99a-97a973508d3d/image.png" alt=""></p>
<h2 id="좌vector-parameter">(좌)Vector Parameter</h2>
<p>Vector Parameter는 색상 같은 벡터 값을 파라미터할 때 쓴다.</p>
<h2 id="우scalar-parameter">(우)Scalar Parameter</h2>
<p>Material Instance, BP, C++에서 바꿀 수 있는 단일 float 값</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/b431bdbf-eeb4-41cc-987c-4adaaebb7be4/image.png" alt=""></p>
<h1 id="material-instance-생성">Material Instance 생성</h1>
<p>Material 우클릭 &gt; Create Material Instance</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/6bc5e098-c672-4904-a2a8-cc4881b84c14/image.png" alt=""></p>
<h1 id="학습포인트">학습포인트</h1>
<h2 id="roughness">Roughness</h2>
<pre><code>0.2 = 번들거림
0.5 = 중간
0.75 = 무광 플라스틱
0.9 = 거의 고무 느낌</code></pre><p>표면의 거칠기를 제어
낮을수록 반사가 선명해지고 높을수록 반사가 퍼진다</p>
<h2 id="specular">Specular</h2>
<pre><code>0.0 = 반사 거의 없음
0.35 = 플라스틱 느낌
0.5 = Unreal 기본값에 가까움
1.0 = 과장된 반사</code></pre><p>비금속 표면의 반사 강도 조절</p>
<h2 id="metallic">Metallic</h2>
<p>오늘은 0으로 고정했는데 1에 가까워질수록 금속처럼 멋있어진다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ CH3 팀프로젝트 63일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-63%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-63%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 02 Jul 2026 02:26:38 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--682시간-34분-">*<em>누적 학습 시간 : 682시간 34분 *</em></h2>
<h4 id="📅-2026-07-01">📅 2026-07-01</h4>
<p>거의 일주일 넘게 동일한 내용을 붙들고 있었고 해결을 못해서 TIL을 쓰지를 못했다.
오늘부터는 진짜 간단하게 쓸 예정 너무 길게 쓰려니까 부담됨.</p>
<p>그리고 어제부터 팀프로젝트를 시작했다.</p>
<p>조 : Team12 RPG
프로젝트 명 : Katana(가제)
목표 : Kannagi Usagi / Sekiro Like와 비슷한 보스전투 구현
기여 파트</p>
<ul>
<li>Enemy</li>
<li>AIController</li>
<li>BB, BT</li>
<li>BTTask</li>
<li>팀 생산성 향상을 위한 UE5 프로젝트 컨벤션 수립 및 Git 워크플로우 최적화</li>
<li>그래픽 리소스 관리 파이프라인 구축(예정)</li>
</ul>
<h1 id="기획">기획</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/b038a8fd-d985-43b2-8118-76033637be97/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/1ef494ed-df5e-4d08-9231-6598ec8cbf3e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ 개인과제 58일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-%EA%B0%9C%EC%9D%B8%EA%B3%BC%EC%A0%9C-58%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-%EA%B0%9C%EC%9D%B8%EA%B3%BC%EC%A0%9C-58%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 25 Jun 2026 11:22:52 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--634시간-34분-">*<em>누적 학습 시간 : 634시간 34분 *</em></h2>
<h4 id="📅-2026-06-24">📅 2026-06-24</h4>
<h1 id="wave-변경을-hud에-표시하기-위한-delegate-연결">Wave 변경을 HUD에 표시하기 위한 Delegate 연결</h1>
<h2 id="현재-프로젝트에서-wave가-시간에-따라-변하는지-확인">현재 프로젝트에서 Wave가 시간에 따라 변하는지 확인</h2>
<p>현재 프로젝트에서는 Wave가 시간 흐름에 따라 변경되고 있다.</p>
<p>Wave 진행의 핵심은 <code>AEnemySpawner</code> 클래스에 있다. <code>AEnemySpawner::Tick()</code>에서 매 프레임 <code>UpdateSpawning(DeltaTime)</code>을 호출하고, 이 함수 안에서 현재 Wave의 경과 시간, 적 스폰 간격, 총 스폰 수, 다음 Wave 대기 시간을 관리한다.</p>
<p>특히 <code>CurrentWaveElapsedTime += DeltaTime</code>으로 현재 Wave의 시간이 누적되고, <code>SpawnInterval</code>마다 적을 스폰한다. 이후 <code>TotalSpawnCount</code>만큼 적을 스폰했고 Wave 완료 조건을 만족하면 <code>CompleteCurrentWave()</code>가 호출된다.</p>
<p>Wave가 완료되면 바로 다음 Wave로 넘어가는 것이 아니라, <code>DelayAfterWave</code>만큼 기다린 뒤 <code>StartWave(CurrentWaveIndex + 1)</code>을 호출한다. 따라서 이 프로젝트의 Wave는 시간과 스폰 상태에 따라 실제로 변화하고 있다.</p>
<h2 id="wave-변경-시점">Wave 변경 시점</h2>
<p>Wave가 실제로 바뀌는 시점은 <code>AEnemySpawner::StartWave()</code> 안이다.</p>
<pre><code class="language-cpp">void AEnemySpawner::StartWave(int32 NewWaveIndex)
{
    if (!WaveData || NewWaveIndex &gt;= WaveData-&gt;GetRuntimeWaveCount())
    {
        StopSpawning();
        OnAllWavesCompleted.Broadcast();
        return;
    }

    CurrentWaveIndex = NewWaveIndex;
    SpawnedCountInCurrentWave = 0;
    CurrentWaveElapsedTime = 0.0f;
    TimeSinceLastSpawn = BIG_NUMBER;
    bWaitingForNextWave = false;
    ActiveWaveEnemies.Reset();

    if (const FBulletHellWaveDefinition* WaveDefinition = GetCurrentWaveDefinition())
    {
        OnWaveStarted.Broadcast(CurrentWaveIndex + 1, WaveDefinition-&gt;WaveName);
    }
}</code></pre>
<p>여기서 중요한 부분은 <code>OnWaveStarted.Broadcast(CurrentWaveIndex + 1, WaveDefinition-&gt;WaveName);</code>이다.</p>
<p><code>CurrentWaveIndex</code>는 내부적으로 0부터 시작하지만, HUD에 보여줄 Wave 번호는 1부터 보여주는 것이 자연스럽기 때문에 <code>CurrentWaveIndex + 1</code>을 전달하고 있다.</p>
<h2 id="이미-존재하는-wave-delegate">이미 존재하는 Wave Delegate</h2>
<p><code>AEnemySpawner</code>에는 이미 Wave 변경을 알릴 수 있는 Delegate가 선언되어 있다.</p>
<pre><code class="language-cpp">DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(
    FBulletHellWaveChangedSignature,
    int32, WaveIndex,
    FName, WaveName
);</code></pre>
<p>그리고 <code>AEnemySpawner</code> 안에 다음 Delegate들이 있다.</p>
<pre><code class="language-cpp">UPROPERTY(BlueprintAssignable, Category = &quot;Enemy Spawner|Wave&quot;)
FBulletHellWaveChangedSignature OnWaveStarted;

UPROPERTY(BlueprintAssignable, Category = &quot;Enemy Spawner|Wave&quot;)
FBulletHellWaveChangedSignature OnWaveCompleted;

UPROPERTY(BlueprintAssignable, Category = &quot;Enemy Spawner|Wave&quot;)
FBulletHellAllWavesCompletedSignature OnAllWavesCompleted;</code></pre>
<p>HUD에 현재 Wave 번호를 보여주려면 새 Delegate를 만들기보다 <code>OnWaveStarted</code>에 바인딩하는 것이 가장 적절하다.</p>
<h2 id="hud에-delegate를-달-위치">HUD에 Delegate를 달 위치</h2>
<p>현재 HUD 구조에서는 <code>UBulletHellHUDPresenter</code>가 HUD Widget과 게임 데이터를 연결하는 역할을 하고 있다.</p>
<p>체력 변경 Delegate와 인벤토리 변경 Delegate도 <code>UBulletHellHUDPresenter::Initialize()</code>에서 바인딩하고 있으므로, Wave 변경 Delegate도 같은 위치에 바인딩하는 것이 구조적으로 자연스럽다.</p>
<pre><code class="language-cpp">void UBulletHellHUDPresenter::Initialize(
    APlayerFighterjet* InPlayer,
    UBulletHellPlayerHUDWidget* InView)
{
    Player = InPlayer;
    View = InView;

    if (!Player || !View)
    {
        return;
    }

    if (UBulletHellHealthComponent* HealthComponent = Player-&gt;GetHealthComponent())
    {
        HealthComponent-&gt;OnHealthChanged.AddDynamic(
            this,
            &amp;UBulletHellHUDPresenter::HandleHealthChanged);
    }

    if (UBulletHellInventoryComponent* InventoryComponent = Player-&gt;GetInventoryComponent())
    {
        InventoryComponent-&gt;OnInventoryChanged.AddDynamic(
            this,
            &amp;UBulletHellHUDPresenter::HandleInventoryChanged);
    }

    for (TActorIterator&lt;AEnemySpawner&gt; It(Player-&gt;GetWorld()); It; ++It)
    {
        EnemySpawner = *It;
        EnemySpawner-&gt;OnWaveStarted.AddUniqueDynamic(
            this,
            &amp;UBulletHellHUDPresenter::HandleWaveChanged);
        break;
    }

    RefreshAll();
}</code></pre>
<p>이 코드를 사용하려면 <code>BulletHellHUDPresenter.cpp</code> 상단에 다음 include가 필요하다.</p>
<pre><code class="language-cpp">#include &quot;EngineUtils.h&quot;
#include &quot;Spawner/EnemySpawner.h&quot;</code></pre>
<h2 id="adddynamic-에러-원인">AddDynamic 에러 원인</h2>
<p><code>AddDynamic(this, &amp;UBulletHellHUDPresenter::HandleWaveChanged)</code>에서 에러가 발생한 이유는 Delegate 시그니처와 연결하려는 함수의 시그니처가 맞지 않았기 때문이다.</p>
<p><code>OnWaveStarted</code>는 <code>int32 WaveIndex</code>, <code>FName WaveName</code> 두 개의 인자를 전달한다.</p>
<pre><code class="language-cpp">DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(
    FBulletHellWaveChangedSignature,
    int32, WaveIndex,
    FName, WaveName
);</code></pre>
<p>따라서 바인딩되는 함수도 동일하게 두 개의 인자를 받아야 한다.</p>
<p>잘못된 형태는 다음과 같다.</p>
<pre><code class="language-cpp">UFUNCTION()
void HandleWaveChanged();</code></pre>
<p>올바른 형태는 다음과 같다.</p>
<pre><code class="language-cpp">UFUNCTION()
void HandleWaveChanged(int32 WaveIndex, FName WaveName);</code></pre>
<h2 id="handlewavechanged-구현">HandleWaveChanged 구현</h2>
<p>HUD Widget에는 이미 <code>OnWaveChanged(int CurrentWave)</code> 함수가 준비되어 있다.</p>
<p>따라서 Presenter의 <code>HandleWaveChanged</code>에서는 전달받은 <code>WaveIndex</code>를 View에 넘겨주면 된다.</p>
<pre><code class="language-cpp">void UBulletHellHUDPresenter::HandleWaveChanged(int32 WaveIndex, FName WaveName)
{
    if (!View)
    {
        return;
    }

    View-&gt;OnWaveChanged(WaveIndex);
}</code></pre>
<p>현재는 Wave 이름을 HUD에 표시하지 않기 때문에 <code>WaveName</code>은 사용하지 않아도 된다. 다만 Delegate 시그니처를 맞추기 위해 인자로는 받아야 한다.</p>
<h2 id="hud-widget의-wave-표시">HUD Widget의 Wave 표시</h2>
<p><code>UBulletHellPlayerHUDWidget</code>에는 이미 Wave 번호를 표시하는 함수가 구현되어 있다.</p>
<pre><code class="language-cpp">void UBulletHellPlayerHUDWidget::OnWaveChanged_Implementation(int CurrentWave)
{
    if (Text_CurrentWave)
    {
        const FText WaveText = FText::Format(
            NSLOCTEXT(&quot;BulletHellHUD&quot;, &quot;WaveFormat&quot;, &quot;Wave {0}&quot;),
            FText::AsNumber(CurrentWave)
        );
        Text_CurrentWave-&gt;SetText(WaveText);
    }
}</code></pre>
<p>따라서 <code>Text_CurrentWave</code>가 Widget Blueprint에서 정상적으로 바인딩되어 있다면, Wave가 시작될 때마다 HUD에 <code>Wave 1</code>, <code>Wave 2</code>, <code>Wave 3</code> 형태로 표시된다.</p>
<h2 id="최종-정리">최종 정리</h2>
<p>현재 프로젝트의 Wave는 <code>AEnemySpawner</code>에서 시간과 스폰 상태에 따라 변경된다.</p>
<p>HUD에 현재 Wave를 표시하기 위해 새 Delegate를 만들 필요는 없다. 이미 존재하는 <code>AEnemySpawner::OnWaveStarted</code>에 HUD Presenter를 바인딩하면 된다.</p>
<p>Delegate를 다는 위치는 <code>UBulletHellHUDPresenter::Initialize()</code>가 가장 적절하다. 이 클래스가 이미 체력, 인벤토리 같은 게임 상태를 HUD Widget에 전달하는 중간 계층 역할을 하고 있기 때문이다.</p>
<p><code>AddDynamic</code> 에러는 <code>HandleWaveChanged</code> 함수가 Delegate와 같은 인자 목록을 갖지 않아서 발생한 것이다. <code>void HandleWaveChanged(int32 WaveIndex, FName WaveName)</code> 형태로 수정하면 해결된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ Landscape 57일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-%EC%A7%84%EB%A1%9C%EA%B3%A0%EB%AF%BC-57%EC%9D%BC%EC%B0%A8-f8ab1mjd</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-%EC%A7%84%EB%A1%9C%EA%B3%A0%EB%AF%BC-57%EC%9D%BC%EC%B0%A8-f8ab1mjd</guid>
            <pubDate>Thu, 25 Jun 2026 11:14:29 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--622시간-34분-">*<em>누적 학습 시간 : 622시간 34분 *</em></h2>
<h4 id="📅-2026-06-23">📅 2026-06-23</h4>
<h1 id="ue5-landscape-material-구성-삽질-정리">UE5 Landscape Material 구성 삽질 정리</h1>
<p>이번 학습의 목표는 <strong>Fab/Quixel 텍스처를 이용해서 Landscape Material Function을 만들고, Layer Paint와 Nanite Displacement까지 적용하는 것</strong>이었다.</p>
<p>처음에는 강의와 노드 생김새가 달라서 단순 버전 차이 문제처럼 보였지만, 실제로는 다음 문제가 연쇄적으로 엮여 있었다.</p>
<pre><code class="language-text">1. Landscape 전용 UV Tiling 구성
2. 기본 더미 텍스처 생성 방식
3. Material Function의 출력 구조
4. Substrate와 Legacy Material Attributes 차이
5. Material Instance Parameter Group 정리
6. Landscape Layer Info Object 생성
7. Weight Blended / No Weight Blending 차이
8. Fab 텍스처의 ORM / ORDp 채널 차이
9. Nanite Tessellation과 Displacement 적용 조건
10. Height 텍스처 없이 ORM을 Displacement로 오용했을 때 발생하는 문제</code></pre>
<p>결론부터 말하면, <strong>Landscape Material과 Layer Paint는 정상적으로 구성했고, Nanite Displacement 파이프라인도 동작 확인했다.</strong> 다만 내가 사용한 Fab 텍스처는 <code>ORM</code>이었기 때문에 <strong>Displacement 채널이 없었다.</strong> 따라서 실제 돌출 표현을 하려면 별도 Height/Displacement 텍스처가 필요하고, 없으면 Displacement는 끄는 쪽이 맞다.</p>
<hr>
<h1 id="landscape-material-function-기본-구조">Landscape Material Function 기본 구조</h1>
<h2 id="landscape-uv-tiling-노드-이해">Landscape UV Tiling 노드 이해</h2>
<p>강의에서 처음 나온 구조는 <code>Landscape Coords</code>에 Scalar 값을 곱해서 텍스처 타일링을 제어하는 방식이었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/bc91f78f-c3ae-4d18-82e3-4579c033a24f/image.png" alt=""></p>
<p>구조는 다음과 같다.</p>
<pre><code class="language-text">Landscape Layer Coords
→ Multiply with Scalar Parameter
→ Tiling</code></pre>
<p>처음에 헷갈렸던 점은 <code>Tiling</code>이라는 노드가 특별한 노드처럼 보였다는 것이다. 실제로는 상황에 따라 <strong>Named Reroute Declaration</strong>이거나 <strong>Function Output</strong>일 수 있다.</p>
<p>중요한 점은 <code>Tiling</code> 값이 <code>0</code>이면 안 된다는 것이다.</p>
<pre><code class="language-text">Landscape UV × 0 = 모든 UV가 거의 0으로 고정</code></pre>
<p>그러면 텍스처가 정상적으로 반복되지 않고 단색처럼 보일 수 있다. 강의에서는 <code>0.2</code>를 사용했고, 테스트할 때는 <code>0.2</code>, <code>1.0</code>, <code>2.0</code> 순서로 비교하는 게 좋다.</p>
<h2 id="기본-더미-텍스처의-정체">기본 더미 텍스처의 정체</h2>
<p>강의에서는 다음 3개의 기본 텍스처를 사용했다.
<img src="https://velog.velcdn.com/images/bak_chun8/post/5827b2a4-233a-4c35-86e9-597b29e76cba/image.png" alt=""></p>
<p>각 텍스처의 역할은 다음과 같다.</p>
<table>
<thead>
<tr>
<th>텍스처</th>
<th>용도</th>
<th>Unreal 설정</th>
</tr>
</thead>
<tbody><tr>
<td><code>T_FlatNormal</code></td>
<td>기본 Normal Map</td>
<td><code>sRGB Off</code>, <code>Compression: Normalmap</code></td>
</tr>
<tr>
<td><code>T_White_Color</code></td>
<td>BaseColor 기본값</td>
<td><code>sRGB On</code>, <code>Compression: Default</code></td>
</tr>
<tr>
<td><code>T_White_LinearColor</code></td>
<td>AO/Roughness/Mask 기본값</td>
<td><code>sRGB Off</code>, <code>Compression: Masks</code> 또는 <code>Default</code></td>
</tr>
</tbody></table>
<p>처음에는 Unreal 에디터 안에서 직접 Texture2D를 만들려고 했지만, 생성 메뉴에는 일반 Texture2D가 없었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/913bb0f4-5c67-4bc5-8bf9-519e55804fde/image.png" alt=""></p>
<p>따라서 일반 Texture2D는 외부에서 PNG를 만든 뒤 import하는 방식으로 처리했다.</p>
<pre><code class="language-text">Photoshop / Krita / 그림판 등에서 1x1 또는 4x4 PNG 생성
→ Content Browser로 드래그 앤 드롭
→ Texture Asset Editor에서 sRGB / Compression Settings 수정</code></pre>
<p>여기서 <code>White_Color</code>와 <code>White_LinearColor</code>는 이미지 자체가 다른 게 아니라 <strong>Unreal에서 색상으로 해석하느냐, 데이터로 해석하느냐</strong>가 다르다.</p>
<hr>
<h1 id="basecolor--ao--roughness--normal-구성">BaseColor / AO / Roughness / Normal 구성</h1>
<h2 id="basecolor-구성">BaseColor 구성</h2>
<p>BaseColor는 텍스처 색과 Vector Parameter를 곱해서 색상 틴트를 줄 수 있게 만들었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/57ce60c8-78d9-46d3-8a84-5f1d64314557/image.png" alt=""></p>
<p>구조는 다음과 같다.</p>
<pre><code class="language-text">BaseColorTex RGB
× LayerA_BaseColor
→ BaseColor</code></pre>
<p><code>LayerA_BaseColor</code>를 흰색으로 두면 원본 텍스처 색이 유지되고, 다른 색을 넣으면 전체 색이 틴트된다.</p>
<h2 id="ao--roughness-구성">AO / Roughness 구성</h2>
<p>AO와 Roughness는 packed texture의 채널을 사용해 Min/Max 범위를 보정하는 방식이었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/a28a7eb2-b9a4-427b-9057-a205d33f3fb0/image.png" alt=""></p>
<p>수식은 다음과 같다.</p>
<pre><code class="language-text">AO = saturate(lerp(AO_Min, AO_Max, AO_Tex))
Roughness = saturate(lerp(Roughness_Min, Roughness_Max, Roughness_Tex))</code></pre>
<p>여기서 <code>Lerp</code>는 Substrate 전용 노드로 대체하는 게 아니라 그대로 사용하는 수학 노드다. Substrate는 최종 셰이딩 구조가 바뀌는 것이고, <code>Lerp</code>, <code>Multiply</code>, <code>Saturate</code> 같은 계산 노드는 그대로 쓴다.</p>
<hr>
<h1 id="material-function-출력-구조와-substrate-차이">Material Function 출력 구조와 Substrate 차이</h1>
<h2 id="내-mf와-강의-mf의-출력-차이">내 MF와 강의 MF의 출력 차이</h2>
<p>처음 만든 MF는 <code>MakeMaterialAttributes</code>를 통해 <code>Result</code> 하나만 출력하고 있었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/bcfffaa1-9c6e-407b-86d9-72a37646a5d3/image.png" alt=""></p>
<p>강의에서는 <code>MF_LandscapeLayerA</code>가 다음처럼 보였다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/f8b675ae-41e4-4e80-aec4-4bf10e55376b/image.png" alt=""></p>
<p>강의의 MF 출력은 다음 2개였다.</p>
<pre><code class="language-text">MA
Displacement</code></pre>
<p>내 MF에서 <code>Displacement</code>가 안 나오던 이유는 단순했다. <code>Dp_Tex</code>를 Named Reroute처럼 빼놓은 것과 <strong>Function Output으로 외부에 내보내는 것</strong>은 다르다.</p>
<p>외부 출력 핀을 만들려면 반드시 다음을 해야 한다.</p>
<pre><code class="language-text">우클릭 → Function Output 생성
Output Name = Displacement
Dp_Tex 또는 Height 계산 결과 연결</code></pre>
<h2 id="substrate-때문에-material-root가-다르게-보임">Substrate 때문에 Material Root가 다르게 보임</h2>
<p>내 프로젝트에서는 Material Root에 <code>Front Material</code> 핀이 보였다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/5a503472-deae-4712-82b6-daadf6b0c785/image.png" alt=""></p>
<p>강의는 Legacy Material Attributes 방식이고, 내 프로젝트는 Substrate가 활성화된 상태였기 때문에 루트 노드 생김새가 달랐다.</p>
<p>이때 선택지는 3개였다.</p>
<table>
<thead>
<tr>
<th>선택지</th>
<th>판단</th>
</tr>
</thead>
<tbody><tr>
<td>강의와 동일하게 Legacy + Use Material Attributes 방식으로 맞춤</td>
<td>최선</td>
</tr>
<tr>
<td>Substrate를 유지하고 <code>Substrate Slab BSDF → Front Material</code> 구조로 다시 구성</td>
<td>차선</td>
</tr>
<tr>
<td><code>MakeMaterialAttributes</code>와 Substrate 루트를 섞어서 억지로 연결</td>
<td>비추천</td>
</tr>
</tbody></table>
<p>이번 학습에서는 강의 흐름을 따라가기 위해 <code>Use Material Attributes</code> 기반으로 정리했다.</p>
<hr>
<h1 id="material-instance-parameter-정리">Material Instance Parameter 정리</h1>
<h2 id="mi-파라미터가-global-그룹으로-흩어진-이유">MI 파라미터가 Global 그룹으로 흩어진 이유</h2>
<p>처음 MI를 열었을 때 파라미터들이 다음처럼 분산되어 있었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/4829f33f-b504-4f3a-b7ed-793c19d82b79/image.png" alt=""></p>
<p>강의에서는 파라미터가 하나의 그룹으로 정리되어 있었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/b57ecf49-d4c8-4258-bab3-ea7918d712c3/image.png" alt=""></p>
<p>원인은 MF 내부 Parameter 노드의 <code>Group</code> 설정이었다.</p>
<pre><code class="language-text">Parameter Name: LayerA_UV_Tiling
Group: LandscapeLayerA
Sort Priority: 원하는 표시 순서</code></pre>
<p>MI 화면에서 고치는 게 아니라, <strong>부모 Material 또는 Material Function 내부의 Parameter 노드</strong>를 고쳐야 한다.</p>
<h2 id="group은-네임스페이스가-아니다">Group은 네임스페이스가 아니다</h2>
<p>중간에는 LayerA 그룹 안에 LayerB 파라미터가 섞여 보였다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/ee5a2855-18ed-4c3b-9d6f-5c144f2c4a4b/image.png" alt=""></p>
<p>여기서 핵심은 다음이다.</p>
<pre><code class="language-text">Group은 표시용 카테고리일 뿐이다.
Parameter Name이 같으면 같은 파라미터로 취급될 수 있다.</code></pre>
<p>잘못된 예:</p>
<pre><code class="language-text">LayerA MF: Tiling
LayerB MF: Tiling</code></pre>
<p>올바른 예:</p>
<pre><code class="language-text">LayerA_UV_Tiling
LayerB_UV_Tiling
LayerC_UV_Tiling</code></pre>
<p>Layer를 복사해서 만들 때는 <strong>Group뿐 아니라 Parameter Name도 전부 Layer별로 고유하게 바꿔야 한다.</strong></p>
<hr>
<h1 id="landscape-paint와-layer-info-문제">Landscape Paint와 Layer Info 문제</h1>
<h2 id="fill-layer가-비활성화된-이유">Fill Layer가 비활성화된 이유</h2>
<p>Landscape Paint에서 <code>Fill Layer</code>가 비활성화되었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/98a7fbb5-1634-48c1-ac02-09b39785d344/image.png" alt=""></p>
<p>에러 메시지는 다음과 같았다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/03cdb18d-8055-4ffb-8b9b-9df8158172bd/image.png" alt=""></p>
<pre><code class="language-text">This layer has no layer info assigned yet.
You must create or assign a layer info before you can paint this layer.</code></pre>
<p>즉 문제는 Material Function이 아니라 <strong>Layer Info Object 미할당</strong>이었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/5abe779c-709a-4439-aa83-bc2983ef383d/image.png" alt=""></p>
<p>각 레이어의 <code>+</code> 버튼을 눌러 Layer Info Object를 생성해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/2d9efa8d-6b39-4f19-a61a-1b33d8b334bf/image.png" alt=""></p>
<h2 id="weight-blended--no-weight-blending">Weight Blended / No Weight Blending</h2>
<p>처음 Layer Info를 만들었을 때 <code>No Weight Blending</code>으로 잡히기도 했다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/db54d140-4b76-4413-a356-ab379b65f564/image.png" alt=""></p>
<p>일반적인 지형 레이어 페인팅이라면 <code>Weight Blending</code>으로 통일하는 편이 낫다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/e6217a5f-9d68-4191-b874-20a0a36fbf31/image.png" alt=""></p>
<p>이번 구조에서는 LayerA, LayerB, LayerC가 서로 섞이는 지형 재질이므로 Weight Blending 계열로 정리했다.</p>
<hr>
<h1 id="fab-텍스처-적용과-품질-차이">Fab 텍스처 적용과 품질 차이</h1>
<h2 id="강의와-내-프로젝트의-layer-preview-차이">강의와 내 프로젝트의 Layer Preview 차이</h2>
<p>강의에서는 Layer 생성 후 미리보기 구체에 바로 텍스처가 적용되어 보였다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/cf8d2e54-ed3f-4586-9b6b-f166319c1e47/image.png" alt=""></p>
<p>내 프로젝트에서는 페인트는 되었지만 결과가 밋밋했다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/28e10bfd-20e6-43ce-b4d6-2d4c5c25a70f/image.png" alt=""></p>
<p>Fab 프리뷰는 훨씬 입체적이었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/117f9f74-f546-43ac-98df-60043d464b20/image.png" alt=""></p>
<p>차이의 원인은 다음으로 정리했다.</p>
<pre><code class="language-text">1. MI에서 실제 텍스처 Override 체크가 안 되어 있을 수 있음
2. Normal Strength가 0이면 입체감이 거의 사라짐
3. AO/Roughness 채널 연결이 틀리면 셰이딩이 이상해짐
4. Fab 프리뷰는 Height/Displacement까지 반영된 경우가 있음
5. 조명과 Auto Exposure 차이도 결과에 영향을 줌</code></pre>
<p>즉 단순히 텍스처 해상도 문제가 아니라 <strong>셰이딩 정보가 제대로 연결되어 있는지</strong>가 더 중요했다.</p>
<hr>
<h1 id="mf--mm-최종-구조">MF / MM 최종 구조</h1>
<h2 id="material-function-전체-구조">Material Function 전체 구조</h2>
<p>최종 MF는 Texture Set, BaseColor, Mask, Normal, Displacement 출력으로 구성했다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/4d7168b8-9c58-49dc-8ca4-cc2ab588bce5/image.png" alt=""></p>
<p>핵심 출력은 다음이다.</p>
<pre><code class="language-text">MakeMaterialAttributes → Function Output: MA
Height/Displacement 계산값 → Function Output: Displacement</code></pre>
<h2 id="mm에서-ma와-displacement-합치기">MM에서 MA와 Displacement 합치기</h2>
<p>MM에서는 <code>MA</code>와 <code>Displacement</code>를 각각 Landscape Layer Blend로 섞은 뒤 <code>SetMaterialAttributes</code>로 합쳤다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/6a101faf-fca7-4605-9dcb-757ed49a841c/image.png" alt=""></p>
<p>처음에는 Displacement용 Layer 이름을 <code>LayerA_Displacement</code>처럼 만들었고, 그 결과 Paint Target Layer가 6개로 늘어났다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/8dc83b6f-033b-4127-a934-20670e22032d/image.png" alt=""></p>
<p>이건 잘못된 구조다.</p>
<p>올바른 구조는 다음이다.</p>
<pre><code class="language-text">MA Blend Layers:
- LayerA
- LayerB
- LayerC

Displacement Blend Layers:
- LayerA
- LayerB
- LayerC</code></pre>
<p>Displacement도 기존 Paint Layer의 weight를 공유해야 하므로, 레이어 이름을 동일하게 맞춰야 한다.</p>
<hr>
<h1 id="nanite-displacement-적용">Nanite Displacement 적용</h1>
<h2 id="ini-설정과-material-설정">ini 설정과 Material 설정</h2>
<p>Nanite Tessellation을 쓰려면 프로젝트 ini에 다음 값을 추가했다.</p>
<pre><code class="language-ini">[/Script/Engine.RendererSettings]
r.Nanite.AllowTessellation=1
r.Nanite.Tessellation=1</code></pre>
<p>그 뒤 에디터를 재시작하고, Material Main Node에서 다음을 설정했다.</p>
<pre><code class="language-text">Enable Tessellation = On
Displacement Center = 0.5
Displacement Magnitude = 작은 값부터 테스트</code></pre>
<p>Landscape Actor에서는 다음도 필요하다.</p>
<pre><code class="language-text">Landscape 선택
→ Enable Nanite
→ Build Data / Rebuild Data</code></pre>
<h2 id="초기-displacement-strength-구조">초기 Displacement Strength 구조</h2>
<p>처음에는 <code>Dp_Tex * Strength</code> 형태로 구성했다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/911fc5b7-6bfd-4b73-a7ad-1f865bebfad4/image.png" alt=""></p>
<p>하지만 실제 변화가 거의 보이지 않았다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/308d1a0e-77ad-493e-ac85-26405c6f7a18/image.png" alt=""></p>
<p>그래서 <code>Constant 10</code>을 넣어 파이프라인 자체가 동작하는지 테스트했다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/b6010a42-ac7a-4e25-9b49-65bc96b519b5/image.png" alt=""></p>
<p>결과적으로 브러시한 영역이 통째로 솟아올랐다. 즉 연결과 Nanite Tessellation 자체는 동작했다.</p>
<pre><code class="language-text">Constant 10으로 솟아오름
= Displacement 파이프라인은 정상
= 문제는 Height 입력값 또는 채널</code></pre>
<hr>
<h1 id="displacement-값-보정과-orm-문제">Displacement 값 보정과 ORM 문제</h1>
<h2 id="centered-displacement-formula">Centered Displacement Formula</h2>
<p>Displacement 값은 0~1 범위를 쓰고, <code>Center = 0.5</code>를 기준으로 움직이는 것이 안정적이다.</p>
<p>그래서 다음 수식으로 바꿨다.</p>
<pre><code class="language-text">(Dp_Tex - 0.5) * DisplacementStrength + 0.5</code></pre>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/4bb8c56e-999f-4112-bb4c-f9aa39e6bb9c/image.png" alt=""></p>
<p>최종 노드 흐름은 다음과 같다.</p>
<pre><code class="language-text">Dp_Tex
→ Subtract 0.5
→ Multiply LayerX_Displacement
→ Add 0.5
→ Saturate
→ Output Displacement</code></pre>
<p>Material Details에서는 Magnitude를 조절했다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/f7ecadf9-8bfd-4872-888d-7d169197962f/image.png" alt=""></p>
<p>하지만 여전히 기대한 것처럼 돌이 튀어나오지는 않았다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/f5af97d5-9c9a-4845-8ad4-421b1952cde6/image.png" alt=""></p>
<h2 id="orm에는-displacement-채널이-없음">ORM에는 Displacement 채널이 없음</h2>
<p>R/G 채널을 Displacement에 넣으면 실제 높이 변화는 생겼지만, 결과가 완전히 비정상적으로 변했다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/50783f13-4655-4e31-9e13-d1de2a9a6331/image.png" alt=""></p>
<p>원인은 텍스처가 <code>ORDp</code>가 아니라 <code>ORM</code>이었다는 점이다.</p>
<pre><code class="language-text">ORM:
R = Ambient Occlusion
G = Roughness
B = Metallic</code></pre>
<p>즉 ORM에는 Height/Displacement 채널이 없다.</p>
<p>따라서 R/G를 DP에 연결하면 다음과 같은 잘못된 결과가 나온다.</p>
<pre><code class="language-text">AO를 높이맵처럼 사용
Roughness를 높이맵처럼 사용
→ 지형이 찢어지고 비정상적으로 솟음</code></pre>
<p>반대로 ORDp 계열이라면 보통 다음처럼 사용한다.</p>
<pre><code class="language-text">ORDp:
R = AO
G = Roughness
B = Displacement / Depth</code></pre>
<p>최종 결론은 다음이다.</p>
<pre><code class="language-text">ORM은 AO/Roughness/Metallic 용도로만 사용한다.
Displacement를 쓰려면 별도 Height/Displacement 텍스처가 필요하다.
Height가 없다면 Displacement는 Constant 0.5로 끄는 편이 맞다.</code></pre>
<hr>
<h1 id="최종-체크리스트">최종 체크리스트</h1>
<h2 id="material-function-체크리스트">Material Function 체크리스트</h2>
<pre><code class="language-text">- Texture Sample Parameter 이름이 LayerA/B/C별로 고유한가?
- Scalar Parameter 이름이 LayerA/B/C별로 고유한가?
- Group이 Layer별로 정리되어 있는가?
- MA는 Function Output인가?
- Displacement도 Function Output인가?
- Displacement 수식은 (Height - 0.5) * Strength + 0.5 구조인가?
- 결과에 Saturate를 걸었는가?</code></pre>
<h2 id="master-material-체크리스트">Master Material 체크리스트</h2>
<pre><code class="language-text">- MA용 Landscape Layer Blend의 Layer 이름: LayerA/B/C
- Displacement용 Landscape Layer Blend의 Layer 이름도 동일하게 LayerA/B/C
- SetMaterialAttributes로 Material Attributes와 Displacement를 합쳤는가?
- Material Main Node에서 Enable Tessellation이 켜져 있는가?
- Magnitude와 Center를 조절했는가?</code></pre>
<h2 id="landscape-체크리스트">Landscape 체크리스트</h2>
<pre><code class="language-text">- LayerA/B/C에 Layer Info Object가 할당되어 있는가?
- Weight Blending 방식이 의도와 맞는가?
- Landscape에 MI가 적용되어 있는가?
- Enable Nanite가 켜져 있는가?
- Build/Rebuild Nanite Data를 했는가?</code></pre>
<h2 id="texture-체크리스트">Texture 체크리스트</h2>
<pre><code class="language-text">- BaseColor는 sRGB On인가?
- Normal은 sRGB Off + Normalmap 압축인가?
- ORM/Mask는 sRGB Off인가?
- ORM을 Displacement로 오용하고 있지 않은가?
- Height/Displacement 텍스처가 실제로 존재하는가?</code></pre>
<hr>
<h1 id="오늘의-결론">오늘의 결론</h1>
<h2 id="최선">최선</h2>
<p>ORM은 AO/Roughness/Metallic 용도로만 쓰고, Displacement는 별도 Height/Displacement 텍스처를 받아서 처리한다. 큰 돌이나 자갈은 Landscape Displacement에 기대기보다 Static Mesh, Foliage, PCG Scatter로 배치하는 것이 맞다.</p>
<h2 id="차선">차선</h2>
<p>Height 텍스처가 없다면 Displacement를 <code>Constant 0.5</code>로 고정해 끄고, Normal/AO/Roughness만 제대로 연결해서 시각 품질을 확보한다.</p>
<h2 id="비추천">비추천</h2>
<p>ORM의 R/G/B 채널을 억지로 Displacement에 넣는 것. 높이 변화는 생기지만 AO/Roughness/Metallic 데이터를 높이로 쓰는 것이므로 지형이 비정상적으로 찢어진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ 진로고민 56일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-%EC%A7%84%EB%A1%9C%EA%B3%A0%EB%AF%BC-56%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-%EC%A7%84%EB%A1%9C%EA%B3%A0%EB%AF%BC-56%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 22 Jun 2026 12:08:08 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--610시간-34분-">*<em>누적 학습 시간 : 610시간 34분 *</em></h2>
<h4 id="📅-2026-06-22">📅 2026-06-22</h4>
<p>매니저님들이나 튜터님들이 TIL을 하나한 보시지는 않는 것 같아서 적어보자면 언리얼 게임 개발자로 취업을 하는 것에 굉장히 회의감을 느끼고있다.</p>
<p>같은 캠프 안에 실력자들을 보고 동경스럽고 배울점도 많아서 캠프자체는 너무 재밌고 유익하다고 생각한다.
하지만 그런 실력자분들 조차 취업이 안돼서 나랑 같은 수업을 듣고있는걸 보면서 너무 많은 고민이 생겼다.</p>
<p>물론 캠프 종료 후 바로 취업이 되는 인원들이 정말 많지 않기때문에 기대는 안하고 있지만 어찌됐든 먹고살아야하는데 안될걸 알지만 일단 한다는 내 성격과는 맞지 않는다.</p>
<p>어떤 것을 해야 다른 사람들보다 강점이 생길지 내가 진짜 더 관심있는 분야가 뭔지를 끊임없이 고민하고 공부해야된다고 생각한다.</p>
<p>냉정하게 자기객관화 했을때 지금은 초급단계라 일정부분 나한테 강점이 있긴 하지만
조금 더 미래를 봤을때 내가 다른사람들보다 게임프로그래밍을 잘할 수 있을까? 고민을 해봤을때는 아니라는 생각이 들었다.</p>
<p>그래서 여러 진로를 탐색해본 결과 VP ICVFX 분야에 관심이 있고 실제로 현장에서 일하는 주변인들도 있어서 앞으로 공부를 해볼 예정이다.</p>
<h1 id="라이브링크">라이브링크</h1>
<h2 id="라이브링크란">라이브링크란?</h2>
<p>현실의 움직임(데이터)를 언리얼 엔진 내부로 실시간으로 꽂아주는 초고속 데이터 빨대라고 생각하면 된다.</p>
<p>예전에는 모션캡처나 카메라 움직임을 쓰려면 외부 프로그램에서 녹화하고 FBX같은 파일로 export 후 언리얼로 가져와야 했기 때문에 리얼타임으로 확인이 불가능했다.</p>
<p>하지만! 라이브링크를 사용하면 실시간 데이터를 리얼타임 렌더링으로 확인 할 수 있게된다.</p>
<h1 id="라이브-링크-구성-3요소">라이브 링크 구성 3요소</h1>
<h2 id="소스">소스</h2>
<p>데이터를 보내주는 출발지이다.
스마트폰 페이스 트래킹/캡처 앱이 될 수도 있고 일반적으로 달려있는 pc의 웹캠이 될 수도 있고 모션캡처 장비가 될 수도 있다.</p>
<h2 id="서브젝트">서브젝트</h2>
<p>소스에서 보내는 데이터 덩어리
예를 들어 모션캡처 장비에서 A캐릭터의 bone데이터, B캐릭터의 bone데이터를 동시에 보낼 때 각각 서브젝트A, 서브젝트B라고 할 수 있다.</p>
<h2 id="클라이언트">클라이언트</h2>
<p>언리얼엔진을 말한다.</p>
<h1 id="그래서-어떻게-사용할건데">그래서 어떻게 사용할건데?</h1>
<p>우선 예제로 언리얼 Livelink와 OpenCV, 웹캠을 사용해서 아이트래커를 통해 언리얼에서 카메라의 회전을 구현해보려고 한다.</p>
<p>작동 방식은 아래와 같다.</p>
<ul>
<li>[현실세계] 나의 얼굴 움직임</li>
<li>[웹캡] 영상 신호 캡쳐</li>
<li>[OpenCV + C++] 영상 분석 -&gt; 눈 좌표 계산</li>
<li>[Live Link Source] (우리가 C++로 코딩할 플러그인) 데이터를 라이브링크 규격으로 변환</li>
<li>[Live Link Client] 언리얼 엔진이 실시간으로 데이터를 </li>
<li>[Cine Camera] 언리얼 속 카메라가 내 눈 위치에 맞춰 실시간으로 이동</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ 개인과제 53일~55일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-%EA%B0%9C%EC%9D%B8%EA%B3%BC%EC%A0%9C-53%EC%9D%BC55%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-%EA%B0%9C%EC%9D%B8%EA%B3%BC%EC%A0%9C-53%EC%9D%BC55%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 19 Jun 2026 12:04:21 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--598시간-34분-">*<em>누적 학습 시간 : 598시간 34분 *</em></h2>
<h4 id="📅-2026-06-19">📅 2026-06-19</h4>
<h1 id="개인과제-구현">개인과제 구현</h1>
<p>3D 1945깔로 만들어보고 있는데 Projectile 발사 에임 보정이 말썽이라 계속 수정하고 있다.</p>
<p>AIController로 적 비행체의 자율적인 움직임을 만들어봤는데 꽤나 만족스럽다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ 개인과제 52일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-%EA%B0%9C%EC%9D%B8%EA%B3%BC%EC%A0%9C-52%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-%EA%B0%9C%EC%9D%B8%EA%B3%BC%EC%A0%9C-52%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 16 Jun 2026 11:59:48 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--562시간-34분-">*<em>누적 학습 시간 : 562시간 34분 *</em></h2>
<h4 id="📅-2026-06-16">📅 2026-06-16</h4>
<h1 id="hdri-backdrop-이용한-우주-배경-구현">HDRI Backdrop 이용한 우주 배경 구현</h1>
<p>사실 어려운건 아니고 우주 공간을 배경으로 하고싶은데 어떻게 하면 쉽고 편하게 할 수 있을지 찾다가 사용한 방법이다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/dec0c737-ae1f-4e7e-a896-0e1ddd838bd0/image.png" alt=""></p>
<p>무려 이정도의 고퀄리티를 쉽게 하는 방법이 있다.</p>
<h2 id="hdr-이미지-구하기">HDR 이미지 구하기</h2>
<p><a href="https://www.spacespheremaps.com/hdr-spheremaps/">이미지 사이트</a>
나는 위 링크에서 HDR이미지를 구했다.
따로 변환할 필요는 없고 해당 이미지를 그대로 import 해주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/b4a99c17-cda6-4bed-a254-9d7b22d96f70/image.png" alt=""></p>
<h2 id="플러그인-설치">플러그인 설치</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/d00a1e9c-3ddd-4aa7-97ad-3290f6797e30/image.png" alt=""></p>
<p>플러그인에서 HDRIBackdrop을 설치하고 에디터를 껏다 키면 아래와 같은 클래스가 추가되고 BP 생성을 해준다.
<img src="https://velog.velcdn.com/images/bak_chun8/post/47325896-14af-413c-8a46-d04a77230e46/image.png" alt=""></p>
<h2 id="레벨에-bp-배치와-cubemap-변경">레벨에 BP 배치와 Cubemap 변경</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/aa415e8f-5767-442e-bc1d-8cdcd75374db/image.png" alt=""></p>
<p>BP만들고 Detail에서 Cubemap을 Import한 HDR 파일로 변경, Size도 늘려주면 오픈월드와 유사하게 매우 큰 공간을 생성할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/0390bacc-ef3d-4830-90bd-facbbf216a75/image.png" alt=""></p>
<p>마지막으로 Detail패널에서 Mesh를 SM_SkySphere로 변경하면 끝이다.</p>
]]></description>
        </item>
    </channel>
</rss>