<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>CatDarkGames.  Game Dev Story</title>
    <link>https://darkcatgame.tistory.com/</link>
    <description>Technical Artist 

dhwlgn12@gmail.com</description>
    <language>ko</language>
    <pubDate>Fri, 17 Jul 2026 00:28:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>CatDarkGame</managingEditor>
    <image>
      <title>CatDarkGames.  Game Dev Story</title>
      <url>https://tistory1.daumcdn.net/tistory/3013695/attach/ed32a2d9467d4351bae83e7f3043922d</url>
      <link>https://darkcatgame.tistory.com</link>
    </image>
    <item>
      <title>붉은 사막 리뷰 - 붉은사막이 오픈월드로서 왜 재밌게 느껴지는지에 대한 구조 분석</title>
      <link>https://darkcatgame.tistory.com/199</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3qjMd/dJMcaiQJ7xk/hPdFV9aNMHrhQ2ybPLcBV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3qjMd/dJMcaiQJ7xk/hPdFV9aNMHrhQ2ybPLcBV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3qjMd/dJMcaiQJ7xk/hPdFV9aNMHrhQ2ybPLcBV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3qjMd%2FdJMcaiQJ7xk%2FhPdFV9aNMHrhQ2ybPLcBV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;662&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 글은 붉은사막을 직접 플레이한 뒤 작성한 개인 리뷰입니다.&lt;br /&gt;특정 개발사나 유저를 비난하기 위한 글이 아니며, 한 명의 플레이어이자 게임 개발자의 관점에서 느낀 경험을 작성했습니다&lt;br /&gt;스포일러 주의: 이 글은 엔딩 이후 콘텐츠, 모든 어비스 해금 이후의 대서고 기록, 세계관의 핵심 설정을 언급합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;2%EC%8B%9C%EA%B0%84-30%EB%B6%84-%EB%A7%8C%EC%9D%98-%ED%99%98%EB%B6%88%EC%97%90%EC%84%9C-134%EC%8B%9C%EA%B0%84-%ED%94%8C%EB%A0%88%EC%9D%B4%EA%B9%8C%EC%A7%80%2C-%EC%8B%A4%ED%8C%A8%EC%99%80-%EA%B3%BC%EC%9E%89%EC%9D%B4-%EB%A7%8C%EB%93%A0-%EC%98%A4%ED%94%88%EC%9B%94%EB%93%9C%EC%9D%98-%EC%97%AD%EC%84%A4&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2시간 30분 만의 환불에서 134시간 플레이까지, 실패와 과잉이 만든 오픈월드의 역설&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;작성 기준: 2026-05-07&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;처음 붉은사막을 플레이했을 때의 결론은 단순했다. 이 게임은 미완성이라고 판단했다. &lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;플레이 두 시간 반 만에 환불했고, 그 판단에는 이유가 있었다. 초반부의 서사는 설득력이 약했고, 기본 튜토리얼은 충분히 작동하지 않았으며, 오픈월드의 시스템들은 서로 맞물리기보다 흩어져 있었다. 그런데 약 2주 뒤 나는 이 게임을 다시 샀고, 결국 134시간 넘게 플레이했다. 이 모순적인 경험이 붉은사막을 설명하는 가장 정확한 출발점이다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;1.-%EC%B2%AB%EC%9D%B8%EC%83%81%3A-%EB%AF%B8%EC%99%84%EC%84%B1%EC%9D%B4%EB%9D%BC%EA%B3%A0-%ED%8C%90%EB%8B%A8%ED%95%9C-%EB%91%90-%EC%8B%9C%EA%B0%84-%EB%B0%98&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1. 첫인상: 미완성이라고 판단한 두 시간 반&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;초반 경험은 강하게 부정적이었다. 구걸하는 NPC에게 돈을 주고 여성 NPC를 구했다는 이유만으로 주인공이 특별취급을 받는 듯한 전개는 낯설었고, 그 낯섦이 의도된 서사적 충격으로 느껴지지 않았다. 플레이어가 세계의 규칙을 충분히 이해하기 전에 사건이 먼저 진행되면서, 인물과 상황을 받아들일 근거가 부족했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적인 플레이 학습도 부실했다. 퍼즐을 풀기 위해 알아야 할 조작과 규칙이 충분히 안내되지 않았고, 그 결과 실패가 도전으로 느껴지기보다 품질 관리의 누락처럼 느껴졌다. 헤르난드에서 기사 보스전을 치른 뒤 주변 월드를 돌아다녔을 때도 판단은 크게 달라지지 않았다. NPC와의 인터랙션은 엉성했고, 범죄 시스템은 설계 의도가 선명하지 않았으며, 적 NPC와 월드 이벤트는 반복 배치된 콘텐츠처럼 보였다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 시점에서 나는 붉은사막은 오픈월드 게임으로서도, 패키지 싱글 게임으로서도 완성되지 않은 결과물로 판단했다. 기술적으로는 야심이 보였지만, 플레이어에게 무엇을 언제 배우게 하고, 어떤 감정선으로 세계를 받아들이게 할지에 대한 기본 설계가 부족했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;2.-%EC%9E%AC%EA%B5%AC%EB%A7%A4%EC%9D%98-%EA%B3%84%EA%B8%B0%3A-%EA%B0%99%EC%9D%80-%EC%BD%98%ED%85%90%EC%B8%A0%EA%B0%80-%EB%B0%98%EB%B3%B5%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%88%8F%ED%8F%BC%EC%9D%98-%EC%9D%B4%EC%83%81%ED%95%A8&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. 재구매의 계기: 같은 콘텐츠가 반복되지 않는 숏폼의 이상함&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 약 2주 뒤 게임을 다시 구매했다. 계기는 인터넷 여론이었다. 특히 쇼츠와 릴스에서 붉은사막 관련 콘텐츠가 계속 등장했는데, 이상하게도 영상들이 서로 같은 내용을 반복하지 않았다. 어떤 영상은 숨겨진 흔적을 보여줬고, 어떤 영상은 예상하지 못한 전투 방법을 보여줬으며, 어떤 영상은 이동, 탈것, 장비 조합, 월드 이벤트의 기묘한 상호작용을 보여줬다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;보통 완성도가 낮은 오픈월드는 단점이 먼저 확산된다. 그런데 붉은사막은 단점과 별개로, 계속 새로운 장면을 만들어내는 힘이 있었다. 그 지점이 다시 확인해볼 가치로 느껴졌다. 첫 플레이에서 내가 본 것이 게임 전체가 아니라, 설명되지 않은 거대한 덩어리의 극히 일부였을 가능성을 인정하게 됐다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;재구매 이후 플레이 시간은 빠르게 늘었다. 약 120시간에 엔딩과 모든 어비스 해금에 도달했고, 이후에도 아직 경험하지 못한 요소가 남아 있어 추가로 14시간 이상을 더 플레이했다. 모든 어비스를 해금한 뒤에는 대서고에서 세계관의 비하인드 스토리까지 읽었다. 그리고 그 순간, 첫 플레이에서 느꼈던 엉성함이 다른 의미로 되돌아왔다.&lt;/p&gt;
&lt;li&gt;참고 : &lt;a style=&quot;letter-spacing: 0px;&quot; href=&quot;https://youtube.com/shorts/ak_SyQlh_6I?si=RCaxa8dG2CBgNEiZ&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtube.com/shorts/ak_SyQlh_6I?si=RCaxa8dG2CBgNEiZ&lt;/a&gt;&lt;/li&gt;
&lt;p&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/ak_SyQlh_6I&quot; width=&quot;572&quot; height=&quot;295&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;3.-%EB%8C%80%EC%84%9C%EA%B3%A0%EC%97%90%EC%84%9C-%EB%8A%90%EB%82%80-%EC%9D%B4%EC%83%81%ED%95%9C-%EC%97%AC%EC%9A%B4&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. &amp;lt;섭리의 대서고&amp;gt;에서 느낀 이상한 여운&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;섭리의 대서고는 붉은 사막의 어비스 지역 중 하나이며, 대서고에서 확인할 수 있는 기록은 현재 플레이어가 도달한 시점까지 어떤 일이 반복되었는지를 설명한다. 그 안에는 최종 보스를 물리치기 위해 반복되었던 세계의 실패가 남아 있다. 108번의 타임루프, 계속된 실패, 데미안이라는 인물을 어떻게 해야 할지 모르겠다는 막막함, 클리프의 감정이 사라진 이유, 이번이 마지막이라는 절박함이 기록되어 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;나에게 이 내용들이 붉은 사막 개발진들의 개발 과정으로 느껴졌다. 이 내용이 실제로 개발진의 자기 고백으로 의도되었다고 단정할 근거는 없다. 그러나 플레이어인 내 경험에서는 그렇게 읽혔다. 게임을 플레이하며 직접 마주했던 엉성한 온보딩, 과잉된 시스템, 연결되지 않는 테마, 기묘하게 많은 콘텐츠, 완성된 것과 미완성인 것이 한 공간에 공존하는 느낌이 대서고의 실패 기록과 겹쳐졌다. 텍스트는 세계관의 기록이었지만, 동시에 개발 과정의 시행착오와 고민, 고통의 흔적처럼 느껴졌다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지 플레이한 게임 중에 개발진의 고생이 이렇게 직접적으로 느껴진 게임은 처음이었다. 이것은 게임 디자인적으로 훌륭했다는 뜻이 아니다. 오히려 반대에 가깝다. 붉은사막은 게임 개발자로서 본받아서는 안 되는 방식의 결과물을 많이 보여준다. 그러나 플레이 유저로서 경험한 전체는 이상하게도 하나의 완성된 기억이 되었다. 결함이 사라진 것이 아니라, 결함까지 포함한 경험이 하나의 정서로 굳어진 것이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;4.-%ED%95%B5%EC%8B%AC-%ED%97%88%EB%93%A4%3A-%EA%B8%B0%EC%88%A0%EB%A0%A5%EA%B3%BC-%EA%B2%8C%EC%9E%84-%EB%94%94%EC%9E%90%EC%9D%B8-%EA%B2%BD%ED%97%98%EC%9D%98-%EB%B6%88%EA%B7%A0%ED%98%95&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;4. 핵심 허들: 기술력과 게임 디자인 경험의 불균형&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;게임 디자인 관점에서 붉은사막의 핵심 허들은 기술력과 오픈월드 디자인 경험의 불균형으로 보인다. 붉은사막은 레드 데드 리뎀션 2, 젤다 야숨, 고스트 오브 쓰시마, 어쌔신 크리드 등 성공한 싱글 오픈월드 패키지 게임의 문법을 폭넓게 참조한 프로젝트로 읽힌다. 그러나 시스템을 많이 만드는 것과, 그 시스템을 플레이어에게 자연스럽게 학습시키고 의미 있는 선택으로 연결하는 것은 다른 역량이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;붉은사막은 탐험, 퍼즐, 전투, 생활, 탈것, 장비, 미니게임, 월드 이벤트 등 콘텐츠 카테고리가 매우 넓다. 문제는 이 콘텐츠들을 어떤 순서로, 어떤 맥락에서, 어느 정도의 가이드와 함께 전달할 것인지가 충분히 정리되지 않았다는 점이다. 게임 플레이 경험에서 온보딩이 중요하며, &lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;자유도를 지향하는 이라도 역설적으로 온보딩을 통한 가이드 필요하다.&lt;/span&gt; 플레이어가 자유롭게 행동하려면 먼저 세계가 어떤 규칙으로 반응하는지 배워야 한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 지점에서 붉은사막은 계속 충돌했을 가능성이 높다. 방대한 시스템을 만들수록 플레이어에게 알려야 할 규칙은 늘어난다. 하지만 가이드를 늘리면 자유로운 탐험감이 줄어든다. 반대로 가이드를 줄이면 플레이어는 콘텐츠를 찾지 못하고 떠난다. 붉은사막은 이 균형을 정교하게 해결했다기보다, 오랜 개발 과정에서 만들어진 콘텐츠를 어떻게든 하나의 월드 안에 밀어 넣은 결과에 가깝게 느껴졌다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;5.-%EC%95%84%EC%9D%B4%EB%9F%AC%EB%8B%88%ED%95%98%EA%B2%8C%EB%8F%84%2C-%EA%B7%B8-%EA%B3%BC%EC%9E%89%EC%9D%B4-%ED%83%90%ED%97%98%EC%9D%98-%EC%9E%AC%EB%AF%B8%EA%B0%80-%EB%90%98%EC%97%88%EB%8B%A4&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;5. 아이러니하게도, 그 과잉이 탐험의 재미가 되었다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 게임의 가장 이상한 지점은 바로 여기다. 유저 온보딩, 스토리 전개, 플레이 가이드가 부족한데도, 세계 안에는 수많은 콘텐츠와 시스템이 남아 있다. 일반적인 선형 게임이라면 이것은 단순한 결함으로 끝났을 것이다. 그러나 붉은사막은 오픈월드다. 설명되지 않은 것들이 월드 곳곳에 흩어져 있고, 플레이어는 우연히 그것을 발견한다. 그러면 결함은 때때로 발견의 재미로 바뀐다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;힌트도 충분하지 않고, 아무도 알려주지 않지만, 돌아다니다 보면 예상하지 못한 보상을 얻는다. 새로운 이동 방식, 숨겨진 상호작용, 전투 빌드, 비밀의 흔적이 계속 튀어나온다. 이 구조는 전통적인 의미에서 좋은 튜토리얼 설계는 아니다. 하지만 2026년의 미디어 환경과 결합하면서 다른 방식으로 작동했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;숏폼 SNS는 붉은사막의 부족한 설명을 외부에서 보완했다. 유저들은 자신이 발견한 장면을 짧은 영상으로 공유했고, 다른 유저들은 그 영상을 보고 다시 게임에 들어가 직접 확인했다. 개발사가 의도적으로 설계한 온보딩은 아니라고 판단하지만, 커뮤니티와 숏폼 플랫폼이 비공식 온보딩처럼 작동한 셈이다. 만약 이런 미디어 환경이 없었다면, 많은 유저는 붉은사막이 가진 콘텐츠의 극히 일부만 보고 게임을 떠났을 것이라고 생각한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;6.-%ED%85%8C%EB%A7%88%EC%9D%98-%EB%82%9C%EB%A7%A5%EC%83%81%EC%9D%B4-%EC%98%A4%ED%9E%88%EB%A0%A4-%EA%B2%BD%ED%97%98%EC%9D%98-%ED%8F%AD%EC%9D%84-%EB%84%93%ED%98%94%EB%8B%A4&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;6. 테마의 난맥상이 오히려 경험의 폭을 넓혔다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;붉은사막의 세계관은 중세 판타지를 기본으로 하지만, 그 안에 동양적 요소, 스팀펑크, 기계 장치, 석유 시추소, 로봇, 제트팩 같은 요소가 함께 들어간다. 세계관 디자인 관점에서 보면 이 조합은 매끄럽지 않다. 각각의 테마가 하나의 네러티브 축으로 정교하게 연결된다고 보기 어렵다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 탐험 경험 관점에서는 이 난맥상이 장점으로도 작동한다. 다음 지역에서 무엇이 나올지 예측하기 어렵고, 그 예측 불가능성이 월드의 기대값을 높인다. 더 중요한 점은 이런 요소들이 배경 장식에 그치지 않는다는 것이다. 플레이어는 로봇을 타고 싸우고, 제트팩으로 날아다니며, 스팀펑크 아머를 입은 드래곤 같은 과격한 이미지를 실제 플레이 경험으로 받아들인다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것은 일관성의 승리가 아니라 폭의 승리다. 붉은사막은 세계관을 정교하게 통제하는 대신, 가능한 많은 경험을 월드 안에 밀어 넣었다. 그 결과 네러티브의 연결성은 약해졌지만, 플레이어가 마주하는 장면과 조작의 범위는 크게 넓어졌다. 바로 이 폭이 숏폼으로 확산되기 좋은 장면들을 계속 만들어냈다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;7.-%EB%A7%88%EC%BC%80%ED%8C%85%EA%B3%BC-%EC%83%81%ED%92%88-%EC%A0%84%EB%9E%B5-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C-%EB%B3%B8-%EC%9D%B4%EB%A1%80%EC%84%B1&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;7. 마케팅과 상품 전략 관점에서 본 이례성&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;마케팅과 상품 전략 관점에서도 붉은사막은 특이한 프로젝트다. 보도 기준으로 개발비만 약 2,000억 원이 언급되는 대형 프로젝트인데, 플레이어 입장에서 체감한 마케팅 접점은 그 규모에 비해 제한적으로 느껴졌다. 물론 실제 마케팅 집행 규모를 외부에서 단정할 수는 없다. 다만 결과물에서 느껴지는 방향성은 명확했다. 이 프로젝트는 시장별 최적화보다 개발물 자체의 밀도와 야심을 우선한 게임처럼 보였다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;게임 내 도박 콘텐츠가 섯다와 도리짓고땡이라는 점은 그 상징적인 사례다. 두 게임 모두 한국 문화권에 익숙한 규칙이며, 특히 도리짓고땡은 국내에서도 대중성이 높은 편이라고 보기 어렵다. 글로벌 유저의 즉각적인 이해를 우선했다면 홀덤처럼 보편성이 높은 룰을 선택하는 편이 자연스럽다. 그런데 붉은사막은 그런 선택을 하지 않았다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;내가 가진 기존 관점은 게임을 디지털 콘텐츠 상품으로 보고, 판매 시장과 고객에 맞춰 서비스와 개발 전략을 세워야 한다는 쪽에 가깝다. 그런 기준에서 보면 붉은사막은 효율적인 상품 설계의 사례가 아니다. 그럼에도 결과적으로 흥행했다. 2026년 4월 15일 기준, 붉은사막은 전 세계 판매량 500만 장을 돌파했다고 발표됐다. 통상적인 효율성의 관점에서는 설명하기 어려운 성과다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 성과에는 여러 요인이 겹쳤다. 한국 개발사의 대형 싱글 패키지 게임이라는 희소성, 현시점 최상위권으로 평가할 수 있는 그래픽과 초원경 비주얼, 숏폼에 적합한 예측 불가능한 콘텐츠, 그리고 출시 이후 유저들이 직접 발견을 공유하는 미디어 환경이 맞물렸다. 결과론적으로 보면 붉은사막은 프로덕션과 마케팅의 정석을 따른 게임이라기보다, 시대적 조건과 과잉 생산물이 우연히 결합해 흥행한 사례에 가깝다고 생각한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;8.-%EA%B2%B0%EB%A1%A0%3A-%EA%B0%90%EC%82%AC%ED%95%98%EC%A7%80%EB%A7%8C-%EB%B0%98%EB%B3%B5%EB%90%98%EC%96%B4%EC%84%9C%EB%8A%94-%EC%95%88-%EB%90%98%EB%8A%94-%EA%B2%8C%EC%9E%84&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;8. 결론: 감사하지만 반복되어서는 안 되는 게임&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결과적으로 붉은사막은 재미있었다. 그리고 글로벌 시장에서도 흥행했다. 하지만 나는 이 개발 방식이 앞으로 반복될 수 있는 방식이라고 판단하지 않는다. 개발비와 인건비가 높아진 상황에서, 장기간의 재작업과 개발자의 체력 소모, 출시 후 재평가, 숏폼을 통한 우연한 발견 확산에 기대는 방식은 건강한 지속 가능한 전략이 아니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;게임 개발자로서 붉은사막은 본받으면 안 되는 지점이 많다. 온보딩은 부족했고, 시스템 통합은 거칠었으며, 서사 전달은 자주 어긋났고, QA 관점에서도 기본적인 플레이 학습 실패가 드러났다. 그러나 플레이어로서의 나는 이 게임을 쉽게 부정할 수 없다. 결함이 많은데도 계속 새로운 장면을 보여줬고, 불친절한데도 계속 돌아다니게 만들었으며, 마지막에는 그 결함마저 세계관의 실패 기록과 겹쳐지는 특이한 여운을 남겼다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;붉은사막은 좋은 프로덕션 모델이 아니다. 하지만 하나의 플레이 경험으로는 이상하게 완성되어 있다. 그것이 이 게임의 가장 큰 모순이자, 내가 2시간 30분 만에 환불했던 게임을 134시간 넘게 플레이하게 된 이유다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내 결론은 이것이다. 붉은사막은 존중하지만 권장할 수 없는 방식으로 만들어진, 실패와 과잉이 함께 완성한 오픈월드다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Misc</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/199</guid>
      <comments>https://darkcatgame.tistory.com/199#entry199comment</comments>
      <pubDate>Thu, 7 May 2026 20:54:56 +0900</pubDate>
    </item>
    <item>
      <title>Unity PostProcess 최적화 - TAA</title>
      <link>https://darkcatgame.tistory.com/198</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1511&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5HcqR/dJMcacCKZp9/R94DwIdNAUXqGqJa3t0t50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5HcqR/dJMcacCKZp9/R94DwIdNAUXqGqJa3t0t50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5HcqR/dJMcacCKZp9/R94DwIdNAUXqGqJa3t0t50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5HcqR%2FdJMcacCKZp9%2FR94DwIdNAUXqGqJa3t0t50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1511&quot; height=&quot;609&quot; data-origin-width=&quot;1511&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 모바일 프로젝트에서 커스텀 TAA를 도입하여 화질을 개선하고 대역폭 최적화를 달성하기까지의 개발 과정을 소개한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘날 모바일 게임에서 고품질 3D 렌더링을 구현하기 위해 Bloom, DoF는 물론 Deferred Rendering, SSAO 등 다양한 후처리 효과가 적극적으로 활용되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 LPDDR5X 기준 중상급~플래그십 전 구간에서도 메모리 대역폭은 64~85 GB/s 수준에 불과하다. 이 대역폭 병목으로 인해 PBR 렌더링과 고품질 후처리를 사용하면서 성능 안정성을 확보하려면, 렌더 스케일을 60~70% 수준으로 낮추는 것이 현실적인 선택이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 렌더 스케일 70% 기준 총 픽셀 수가 네이티브 대비 약 49%로 줄어들며, 단순 Bilinear 업스케일만으로는 화면 흐림과 외곽선 계단 현상을 피할 수 없다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TAA 도입을 통해 업스케일 효과 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Temporal Anti-Aliasing(TAA)은 매 프레임 투영 행렬에 서브픽셀 단위의 오프셋(jitter)을 적용하여, 여러 프레임에 걸쳐 서로 다른 서브픽셀 위치를 누적 합성하는 기법이다. 단일 프레임으로는 부족한 해상도를 시간 축의 누적을 통해 복원할 수 있으며, DLSS, FSR 2+ 등 최신 업스케일 기술들도 이 프레임 누적 원리를 기반으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. TAA의 원리와 한계&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 기본 동작 원리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TAA의 원리 자체는 단순하다. 매 프레임 투영 행렬에 서브픽셀 단위의 오프셋(jitter)을 적용해 카메라를 미세하게 흔들고, 그 결과를 히스토리 버퍼에 누적 합성한다. 단일 프레임에서는 픽셀 중심 한 점만 샘플링하지만, 수십 프레임이 누적되면 픽셀 내부의 서로 다른 서브픽셀 위치가 커버되어 AA 및 업스케일링 효과가 자연스럽게 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오프셋은 Halton 시퀀스(base 2, 3)로 생성하여 [-0.5, +0.5] 픽셀 범위 내에 준-균일하게 분포시킨다. 히스토리 블렌딩은 아래와 같이 이전 누적 결과에 높은 가중치를 부여하는 방식으로 동작한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZDA1.gif&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CvQyn/dJMcaf7hNsB/hb8ISFx44nvmaYwfPIhA0K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CvQyn/dJMcaf7hNsB/hb8ISFx44nvmaYwfPIhA0K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CvQyn/dJMcaf7hNsB/hb8ISFx44nvmaYwfPIhA0K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/CvQyn/dJMcaf7hNsB/hb8ISFx44nvmaYwfPIhA0K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;373&quot; data-filename=&quot;ZDA1.gif&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 근본적 한계 &amp;mdash; Ghosting과 Disocclusion&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원리는 단순하지만, TAA의 실제 기술력은 히스토리 누적 방식에서 발생하는 두 가지 아티팩트를 얼마나 잘 억제하느냐에 집중된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ghosting&lt;/b&gt;은 히스토리 버퍼의 이전 정보가 현재 프레임과 불일치할 때 발생하는 잔상이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트가 빠르게 이동하면 이전 위치의 색상이 반투명하게 겹쳐 보인다. 이를 해결하기 위해 Motion Vector로 히스토리를 현재 픽셀 위치로 재투영(reprojection)하고, Color Clamping으로 히스토리 색상이 현재 프레임 이웃 픽셀의 색상 범위를 벗어나면 강제로 끌어온다. 다만 clamping이 과도하면 flickering이 발생하고, 느슨하면 ghosting이 남는 트레이드오프가 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZDA2.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQHxxZ/dJMcahqwMlH/9ZPKYlzqIg3CV4ZAz1ryLk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQHxxZ/dJMcahqwMlH/9ZPKYlzqIg3CV4ZAz1ryLk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQHxxZ/dJMcahqwMlH/9ZPKYlzqIg3CV4ZAz1ryLk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cQHxxZ/dJMcahqwMlH/9ZPKYlzqIg3CV4ZAz1ryLk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;347&quot; data-filename=&quot;ZDA2.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Disocclusion&lt;/b&gt;은 이전 프레임에서 다른 오브젝트에 가려져 있던 픽셀이 현재 프레임에서 새로 노출되는 픽셀을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;depth 차이 기반 감지, velocity 크기 기반 감지 등 다양한 휴리스틱이 사용되지만, 해당 영역이 이전 프레임에서 아예 보이지 않았던 영역이기 때문에 참조할 유효한 히스토리 데이터 자체가 존재하지 않는다는 프레임 누적 방식의 근본적인 한계다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZDA23.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7dkjX/dJMcacirEMs/S1rWxGl8z1iQP1gDOUrYyk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7dkjX/dJMcacirEMs/S1rWxGl8z1iQP1gDOUrYyk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7dkjX/dJMcacirEMs/S1rWxGl8z1iQP1gDOUrYyk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/7dkjX/dJMcacirEMs/S1rWxGl8z1iQP1gDOUrYyk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;347&quot; data-filename=&quot;ZDA23.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 TAA 구현의 핵심은 Motion Vector, Color Clamping, Disocclusion 감지 휴리스틱을 조합하여 이 아티팩트들을 얼마나 효과적으로 억제하느냐에 달려 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;3.-urp-%EB%82%B4%EC%9E%A5-taa-%E2%80%94-%EC%86%8C%EA%B0%9C%EC%99%80-%ED%95%9C%EA%B3%84%EC%A0%90&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. URP 내장 TAA &amp;mdash; 소개와 한계점&lt;/b&gt;&lt;/h2&gt;
&lt;h3 id=&quot;3.1-urp-taa-%EA%B5%AC%EC%A1%B0-%EA%B0%9C%EC%9A%94&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3.1 URP TAA 구조 개요&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Unity 6의 URP 17.3.0에는 빌트인 TAA가 포함되어 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;MotionVectorRenderPass&lt;/b&gt;: Camera MV(풀스크린) + Object MV(RendererList)를 하나의 렌더 패스에서 생성&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;TemporalAA 정적 클래스&lt;/b&gt;: 포스트프로세싱 파이프라인 내부에서 직접 호출되는 TAA 처리&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;TaaHistory&lt;/b&gt;: double buffering 기반 히스토리 관리&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;Contrast Adaptive Sharpening&lt;/b&gt; : TAA 옵션에서 값을 0보다 높게 설정하면 FinalPost Pass에서 처리.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;5단계 품질 티어를 제공하며, 각 티어는 알고리즘 파라미터 조합으로 구현된다.&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;품질&lt;/td&gt;
&lt;td&gt;색공간&lt;/td&gt;
&lt;td&gt;Clamping&lt;/td&gt;
&lt;td&gt;Depth Dilation&lt;/td&gt;
&lt;td&gt;History 샘플링&lt;/td&gt;
&lt;td&gt;Center Filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VeryLow&lt;/td&gt;
&lt;td&gt;RGB&lt;/td&gt;
&lt;td&gt;5탭 min/max&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;Bilinear&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa;&quot;&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;RGB&lt;/td&gt;
&lt;td&gt;5탭 min/max&lt;/td&gt;
&lt;td&gt;5탭&lt;/td&gt;
&lt;td&gt;Bilinear&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;YCoCg&lt;/td&gt;
&lt;td&gt;9탭 Variance&lt;/td&gt;
&lt;td&gt;9탭&lt;/td&gt;
&lt;td&gt;Bilinear&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa;&quot;&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;YCoCg&lt;/td&gt;
&lt;td&gt;9탭 Variance&lt;/td&gt;
&lt;td&gt;9탭&lt;/td&gt;
&lt;td&gt;Bicubic 5탭&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VeryHigh&lt;/td&gt;
&lt;td&gt;YCoCg&lt;/td&gt;
&lt;td&gt;AABB Clip&lt;/td&gt;
&lt;td&gt;9탭&lt;/td&gt;
&lt;td&gt;Bicubic 5탭&lt;/td&gt;
&lt;td&gt;Blackman-Harris&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Medium 이상에서는 YCoCg 색공간 변환, Variance Clamping, Perceptual Space 블렌딩(1/(luma+1)&lt;span&gt;&amp;nbsp;&lt;/span&gt;Reinhard-like 가중치), Playdead의 AABB Clip 등 학술적으로 검증된 고급 기법들이 적용된다. 이들은 HDRP 구현을 참조하여 URP에 맞게 경량화한 것으로, 많은 프로덕션 타이틀에서 충분히 효과적으로 동작하는 검증된 조합이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;3.2-urp-taa%EC%9D%98-%ED%95%9C%EA%B3%84%EC%A0%90&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3.2 URP TAA의 한계점&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이론적으로 잘 설계된 URP TAA지만, 모바일 프로젝트에서 사용할 때 몇 가지 한계에 부딪힌다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;① 고정 블렌드 팩터 &amp;mdash; Clamping에만 의존하는 Ghosting 제어&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;URP TAA는 화면 전체에 동일한 블렌드 팩터(_TaaFrameInfluence, 기본 0.1)를 적용한다. 이는 의도적인 설계로, 콘텐츠에 무관하게 안정적이고 예측 가능한 동작을 제공한다는 장점이 있다. 반면, 빠른 움직임이 있는 장면에서도 동일한 히스토리 비율(90%)이 적용되므로, ghosting 제어를 전적으로 Color Clamping의 품질에 의존한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Variance Clamping과 AABB Clip은 대부분의 시나리오에서 효과적으로 ghosting을 억제하지만, 고채도 이펙트나 반투명 파티클이 겹치는 극단적 상황에서는 잔상이 완전히 제거되지 않을 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;② MotionVector 포맷 &amp;mdash; 모바일 대역폭 부담&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;URP는&lt;span&gt;&amp;nbsp;&lt;/span&gt;R16G16_SFloat(4바이트/픽셀) 포맷으로 Motion Vector를 저장한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모바일의 제한된 대역폭에서 FHD 기준 약 8.3MB의 추가적인 프레임버퍼 부담이 된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;③ Disocclusion 감지 로직이 존재하지 않음&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;URP TAA는 History UV가 화면 밖을 벗어나는 경우를 체크할 뿐, 오브젝트 이동에 의한 일반적 disocclusion을 감지하는 로직이 존재하지 않는다. Depth dilation이 전경/배경 경계에서 올바른 MV를 선택하도록 도와주어 간접적으로 일부 케이스를 완화하지만, disocclusion에 의한 아티펙트를 완벽하게 해결 할 수 없다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;④ Particle System 및 MotionVector 정보 없는 상황 대응 불가&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Unity 6000.3.10f1 버전까지도 Particle System에서 렌더링되는 Shader에는 MotionVector 정보가 들어오지 않는다. 또한 UVScroll, Vertex Animation 등, 결과적으로 Ghosting &amp;amp; Disocclusion을 처리할 수 없는 상황에도 URP TAA는 여전히 해당 픽셀에 대해 히스토리와 블렌딩이 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;4.-%EC%BB%A4%EC%8A%A4%ED%85%80-taa-%E2%80%94-history-rejection-%EC%A4%91%EC%8B%AC%EC%9D%98-%EC%A0%91%EA%B7%BC&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 커스텀 TAA &amp;mdash; History Rejection 중심의 접근&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Ghosting의 원인을 분석하면, 대부분 History 정보가 존재하지 않는 Disocclusion 픽셀과 UVScroll, ParticleSystem 등, MotionVector 정보가 존재하지 않는 픽셀에서 발생하며, URP TAA이런 문제 픽셀에 대해서 복잡한 Color Clamping 알고리즘으로 해결하려고 한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;커스텀 TAA의 핵심 컨셉은 동적 오브젝트에 대한 이전 &amp;amp; 현재 프레임 Mask 정보를 기록하여 TAA Pass에서 Mask 비교를 통해 Disocclusion을 판정 -&amp;gt; History Rejection(히스토리 버퍼 블랜딩 차단)한다.&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;4.1-motionmask-%EC%8B%9C%EC%8A%A4%ED%85%9C-%E2%80%94-ghosting%EC%9D%98-%EA%B7%BC%EB%B3%B8-%EC%B0%A8%EB%8B%A8&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4.1 MotionMask 구현&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;ObjectMotionVector 렌더링 시, 두 번째 렌더 타겟(&lt;/span&gt;SV_Target1&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;)에 R8 포맷의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;MotionMask&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;를 동시에 출력한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;이를 위해 ObjectMotionVector Shader Pass를 커스텀으로 제작 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AxjlK/dJMcadhlcFQ/zvMEj7zLhLwyz7Hs0e7zr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AxjlK/dJMcadhlcFQ/zvMEj7zLhLwyz7Hs0e7zr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AxjlK/dJMcadhlcFQ/zvMEj7zLhLwyz7Hs0e7zr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAxjlK%2FdJMcadhlcFQ%2FzvMEj7zLhLwyz7Hs0e7zr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;121&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;MotionMask RT는 이전 &amp;amp; 현재 프레임을 스위칭하며 렌더링해야하기 때문에 URP HistoryManager를 통해 관리하도록 해야하며, 커스텀 MotionVectorPass에서 MotionVector RT와 함께 MRT로 렌더링한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ViChv/dJMcabDP0b1/YxiDVvceM5Huxtj1B5HKu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ViChv/dJMcabDP0b1/YxiDVvceM5Huxtj1B5HKu1/img.png&quot; data-alt=&quot;커스텀 Motion Vector ScriptableRenderPass&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ViChv/dJMcabDP0b1/YxiDVvceM5Huxtj1B5HKu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FViChv%2FdJMcabDP0b1%2FYxiDVvceM5Huxtj1B5HKu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1109&quot; height=&quot;434&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;커스텀 Motion Vector ScriptableRenderPass&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ObjectMotionVector Shader에서는 기존 MotionVector(Velocity) 처리와 추가로 Mask 정보를 기록한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ObjectMotionVector Pass는 기본적으로 SkinnedMesh는 자동으로 호출되며 Mesh Renderer는 아래 조건으로 호출된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. Transform이 변동됨.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. Motion Vectors 옵션이 PerObject Motion 혹은 Force No Motion.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MotionVectors 옵션 값은 &lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;unityMotionVectorParams.y&quot; 글로벌 변수로 Shader에서 읽을 수 있으며 MotionMask RT에는 이 값에 따라 아래와 같이 Mask를 기록한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;MotionMask RT에 기록한 정보는 이후 TAA Pass에서 Disocclusion 감지 및 History Rejection 판정으로 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start; border-collapse: collapse; width: 100%; height: 63px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 15.814%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;Mask 값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.3953%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;MotionVectors 세팅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.6744%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa; height: 21px;&quot;&gt;
&lt;td style=&quot;width: 15.814%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;0.5&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.3953%; text-align: center; height: 21px;&quot;&gt;Per-Object Motion&lt;/td&gt;
&lt;td style=&quot;width: 52.6744%; text-align: center; height: 21px;&quot;&gt;SkinnedMesh, 동적 오브젝트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 15.814%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt;1.0&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.3953%; text-align: center; height: 21px;&quot;&gt;Force No Motion&lt;/td&gt;
&lt;td style=&quot;width: 52.6744%; text-align: center; height: 21px;&quot;&gt;파티클, UV 스크롤 등 MV 기록 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EAxQX/dJMcaaEWV7m/KlCpLIvjWtuAw7YbSCe4x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EAxQX/dJMcaaEWV7m/KlCpLIvjWtuAw7YbSCe4x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EAxQX/dJMcaaEWV7m/KlCpLIvjWtuAw7YbSCe4x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEAxQX%2FdJMcaaEWV7m%2FKlCpLIvjWtuAw7YbSCe4x0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;679&quot; height=&quot;141&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1103&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by6pQs/dJMcadBzbUk/mg9xRWKMnVXPw2c9mV8lGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by6pQs/dJMcadBzbUk/mg9xRWKMnVXPw2c9mV8lGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by6pQs/dJMcadBzbUk/mg9xRWKMnVXPw2c9mV8lGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby6pQs%2FdJMcadBzbUk%2Fmg9xRWKMnVXPw2c9mV8lGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;279&quot; data-origin-width=&quot;1103&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1571&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y6Dxb/dJMcaa50hiv/KwaJOKXtYqmm38QpuY01G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y6Dxb/dJMcaa50hiv/KwaJOKXtYqmm38QpuY01G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y6Dxb/dJMcaa50hiv/KwaJOKXtYqmm38QpuY01G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy6Dxb%2FdJMcaa50hiv%2FKwaJOKXtYqmm38QpuY01G1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;378&quot; data-origin-width=&quot;1571&quot; data-origin-height=&quot;884&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;4.1-motionmask-%EC%8B%9C%EC%8A%A4%ED%85%9C-%E2%80%94-ghosting%EC%9D%98-%EA%B7%BC%EB%B3%B8-%EC%B0%A8%EB%8B%A8&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4.2 Disocclusion 감지 &amp;amp; History Rejection&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TAA Pass에서 현재 &amp;amp; 이전 MotionMask RT를 샘플링하여 History Rejection 판정한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;이 접근의 핵심은 복잡한 &quot;clamping으로 잘못된 히스토리를 보정하는 것이 아니라, 무효한 히스토리를 아예 사용하지 않는 것&quot;이다. Disocclusion 픽셀 &amp;amp; Force No Motion 픽셀을 히스토리를 즉시 폐기하므로, 해당 영역은 TAA의 History Blending 효과를 받지 못한다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;빠르게 움직이는 오브젝트 및 Particle System, UVScroll 등, MotionVector가 없는 픽셀에서 TAA 누적 효과는 의미 없으며, ghosting이 없는 깨끗한 원본 색상 버퍼 출력이 더 깔끔하게 나온다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776074256628&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// TemporalAA_AccumulatePass
half currentMask = SAMPLE_TEXTURE2D_LOD(_MotionMaskTexture, sampler_LinearClamp, uvData.depthOffsetUV, 0.0).r;
[branch] if (currentMask &amp;gt; 0.5) 
{
    // History Full Rejection
    return half4(colorData.center.rgb, 1.0);  
}

CalculateReprojectedUV(motionVector, uvData);

half prevMask = SAMPLE_TEXTURE2D_LOD(_PrevMotionMaskTexture, sampler_LinearClamp, uvData.reprojectedUV, 0.0).r;
bool isCurrentStatic = currentMask &amp;lt; CONST_MASK_THRESHOLD; // 현재 프레임 픽셀이 고정(배경) 픽셀
bool isPrevDynamic = prevMask &amp;gt; CONST_MASK_THRESHOLD;      // 이전 프레임 픽셀이 동적 픽셀
    
[flatten]
if (isCurrentStatic &amp;amp;&amp;amp; isPrevDynamic)  
{
    // Disocclusion
    // 현재 프레임이 고정 픽셀인데, 이전 프레임이 동적 픽셀 = 이번 프레임에 새로 보이는 픽셀
}
else
{
    // History Blending
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;asdqweqweqw.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHnAHc/dJMcaiJGi9W/jvGZNBZf6c95E5kcpKGWa1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHnAHc/dJMcaiJGi9W/jvGZNBZf6c95E5kcpKGWa1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHnAHc/dJMcaiJGi9W/jvGZNBZf6c95E5kcpKGWa1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bHnAHc/dJMcaiJGi9W/jvGZNBZf6c95E5kcpKGWa1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;349&quot; data-filename=&quot;asdqweqweqw.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;초록 = History Blending 정상 작동.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;빨강 = Disocclusion&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;파랑 = Force No Motion&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;4.1-motionmask-%EC%8B%9C%EC%8A%A4%ED%85%9C-%E2%80%94-ghosting%EC%9D%98-%EA%B7%BC%EB%B3%B8-%EC%B0%A8%EB%8B%A8&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4.3 TAA 구현&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Color Clamping / Clipping&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Color Clamping은 TAA Pass에서 &lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;History 색상을 현재 프레임의 이웃 색상 범위로 제한하여 고스팅을 방지하는 기본적인 기법이다. 커스텀 TAA에서는 기본적으로 4방향 ~ 9방향 주변 픽셀 색상 Clamping 처리만 하며, YCoCg 색공간 변환, Playdead ClipToAABB 등, 복잡한 연산의 처리는 하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buxz8r/dJMcaflWeQQ/StKg9PdMfe1i0nWh4h74gK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buxz8r/dJMcaflWeQQ/StKg9PdMfe1i0nWh4h74gK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buxz8r/dJMcaflWeQQ/StKg9PdMfe1i0nWh4h74gK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbuxz8r%2FdJMcaflWeQQ%2FStKg9PdMfe1i0nWh4h74gK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1210&quot; height=&quot;410&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start; border-collapse: collapse; width: 100%; height: 181px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 21px; width: 17.7907%;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px; width: 44.3023%;&quot;&gt;&lt;b&gt;URP TAA&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 21px; width: 37.7907%;&quot;&gt;&lt;b&gt;커스텀 TAA&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 17.7907%;&quot;&gt;기본 방식&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 44.3023%;&quot;&gt;Min/Max AABB Clamp&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 37.7907%;&quot;&gt;Min/Max Clamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa; height: 38px;&quot;&gt;
&lt;td style=&quot;height: 38px; width: 17.7907%;&quot;&gt;Variance Clamping&lt;/td&gt;
&lt;td style=&quot;height: 38px; width: 44.3023%;&quot;&gt;Medium 이상 (9-tap, 설정 가능)&lt;/td&gt;
&lt;td style=&quot;height: 38px; width: 37.7907%;&quot;&gt;High에서만 (9-tap, 고정 gamma=1.25)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 42px;&quot;&gt;
&lt;td style=&quot;height: 42px; width: 17.7907%;&quot;&gt;고급 Clipping&lt;/td&gt;
&lt;td style=&quot;height: 42px; width: 44.3023%;&quot;&gt;VeryHigh에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;ClipToAABBCenter&lt;span&gt;&amp;nbsp;&lt;/span&gt;(Playdead 방식)&lt;/td&gt;
&lt;td style=&quot;height: 42px; width: 37.7907%;&quot;&gt;미적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa; height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 17.7907%;&quot;&gt;색공간&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 44.3023%;&quot;&gt;YCoCg에서 수행 (더 정확한 색상 범위 계산)&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 37.7907%;&quot;&gt;RGB에서 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.7907%;&quot;&gt;Perceptual Space&lt;/td&gt;
&lt;td style=&quot;width: 44.3023%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;Reinhard-style 톤매핑 가중치 적용&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.7907%;&quot;&gt;미적용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1.4-depth-dilation-(motion-dilation)&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Depth Dilation (Motion Dilation)&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가장 가까운 깊이의 이웃 픽셀을 찾아 해당 위치의 MotionVector를 사용하는 기법이다. 배경 픽셀의 MotionVector가 전경 오브젝트 경계를 오염시키는 문제를 방지한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Depth Dilation 알고리즘은 URP 내장과 동일하지만, [flatten]&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;어트리뷰트로 분기를 명시적으로 작성하여, 예상치 못한 branch 컴파일로 인해 GPU 워프 다이버전스 발생을 방지한다.&lt;/span&gt; &lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciiba4/dJMb990lMkN/dagRycRsoU3C1f4NRvnrV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciiba4/dJMb990lMkN/dagRycRsoU3C1f4NRvnrV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciiba4/dJMb990lMkN/dagRycRsoU3C1f4NRvnrV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fciiba4%2FdJMb990lMkN%2FdagRycRsoU3C1f4NRvnrV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;933&quot; height=&quot;890&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1.4-depth-dilation-(motion-dilation)&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Color &amp;amp; Depth 이웃 픽셀 UV Vertex Stage에서 계산&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fragment Stage에서 ALU 연산을 최대한 줄이기 위해 주변 픽셀 UV 정보는 Vertex Stage에서 계산하여 &lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;Interpolator로 전달한다. 이 UV는 Color &amp;amp; Depth 이웃 픽셀 샘플링에 그대로 사용된다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wvBjH/dJMcafztQv3/2vQUFXYAVve3jOTk3KkXCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wvBjH/dJMcafztQv3/2vQUFXYAVve3jOTk3KkXCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wvBjH/dJMcafztQv3/2vQUFXYAVve3jOTk3KkXCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwvBjH%2FdJMcafztQv3%2F2vQUFXYAVve3jOTk3KkXCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;608&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1.3-history-blending-%EC%A0%84%EB%9E%B5&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;History Blending&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;History Blending 과정은 현재 ColorRT와 History ColorRT의 블랜딩 비율을 결정하는 과정이다. 정적 화면 기준으로 History ColorRT의 비율을 높이면 TAA 효과를 더 많이 받을 수 있지만, 동적 화면에서는 누적 색상 정보가 부족하여 오히려 화면이 흐릿해지는 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP TAA는 이 비율 결정을 Camera TAA 설정에서 결정하는 것 외에 TAA Pass에서 따로 처리하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 TAA는 MotionVector 정보를 기반으로 정적 픽셀과 동적 픽셀의 움직임 강도에 따라 동적으로 제어하도록하여 정적 픽셀을 History Blending 비율을 높이고, 동적 픽셀은 현재 ColorRT의 비율을 높여 흐릿해지는 현상을 크게 완화했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/veBkE/dJMcaiXdjuA/DDjLkI6PPOEJcfq3ojoaYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/veBkE/dJMcaiXdjuA/DDjLkI6PPOEJcfq3ojoaYk/img.png&quot; data-alt=&quot;URP TAA&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/veBkE/dJMcaiXdjuA/DDjLkI6PPOEJcfq3ojoaYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FveBkE%2FdJMcaiXdjuA%2FDDjLkI6PPOEJcfq3ojoaYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;904&quot; height=&quot;115&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;URP TAA&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 TAA History Blending 로직&lt;/p&gt;
&lt;pre id=&quot;code_1776077007797&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float CalculateMotionBlend(float2 motionVector)
{
    float motionLength = sqrt(dot(motionVector, motionVector));
    motionLength = saturate(motionLength * PARAM_MOTION_SENSITIVE_SCALE);
    float blendRange = PARAM_MOTION_BLENDFACTOR_BASE - PARAM_MOTION_BLENDFACTOR_MOTION;
    return motionLength * blendRange + PARAM_MOTION_BLENDFACTOR_MOTION;
}

half3 historyColor = SampleHistoryColor(uvData.reprojectedUV);
half3 clampedHistory = ClampHistory(historyColor, colorData);
float blendFactor = CalculateMotionBlend(motionVector);
finalColor = lerp(sharpenedColor, clampedHistory, blendFactor);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Specular Flickering 완화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 TAA는 Color Clamping에 대한 처리가 단순화되고&amp;nbsp; Perceptual Space Blending 또한 처리하지 않게 되면서, HDR 컬러에서 밝은 픽셀에 대한 블랜딩 완화되지 않아 밝고 작은 Specular에 대한 Flickering 이슈 발생 위험성이 높아지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이슈는 경량화된 커스텀 TAA의 구조적이 한계점으로, 오브젝트 Shader에 Specular Anti-Aliasing 로직을 추가하여 해결했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;1150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y6Nl4/dJMcahKPCUs/RmzY3UEJUrxdHGdQC6apy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y6Nl4/dJMcahKPCUs/RmzY3UEJUrxdHGdQC6apy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y6Nl4/dJMcahKPCUs/RmzY3UEJUrxdHGdQC6apy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY6Nl4%2FdJMcahKPCUs%2FRmzY3UEJUrxdHGdQC6apy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;808&quot; height=&quot;454&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;1150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBktlT/dJMcabDP43b/GKx8bUMamkSKA8koyqv86K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBktlT/dJMcabDP43b/GKx8bUMamkSKA8koyqv86K/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;343&quot; data-filename=&quot;Before_SAA_Zoom.gif&quot; style=&quot;width: 50.5725%; margin-right: 10px;&quot; data-widthpercent=&quot;51.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBktlT/dJMcabDP43b/GKx8bUMamkSKA8koyqv86K/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBktlT%2FdJMcabDP43b%2FGKx8bUMamkSKA8koyqv86K%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;343&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqlLuK/dJMcajofKEP/KNVn4sE2BvccZXGCitJx11/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqlLuK/dJMcajofKEP/KNVn4sE2BvccZXGCitJx11/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;360&quot; data-filename=&quot;After_SAA_Zoom.gif&quot; style=&quot;width: 48.2648%;&quot; data-widthpercent=&quot;48.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqlLuK/dJMcajofKEP/KNVn4sE2BvccZXGCitJx11/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqlLuK%2FdJMcajofKEP%2FKNVn4sE2BvccZXGCitJx11%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : Before, 우 : After&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1.6-sharpening&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Sharpening&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;TAA는 본질적으로 여러 프레임의 샘플을 블렌딩하므로 결과가 약간 흐릿해지는 경향이 있다. URP는 이를 TAA Pass 이후에 Final Post Pass에서 CAS(Contrast Adaptive Sharpening)로 처리하지만, 커스텀 TAA에서는 TAA Pass 내부에서&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Laplacian 기반 샤프닝&lt;/b&gt; 처리하도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776077494510&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;half3 SharpeningFilter(half3 colorCenter, half3 neighborSum, half intensity)
{
    const half avg = TAA_QUALITY_HIGH ? 0.125 : 0.25;  // 8탭 vs 4탭
    half3 laplacian = (colorCenter - (neighborSum * avg)) * intensity;
    half3 sharpened = mad(laplacian, CONST_E_CONSTANT, colorCenter);  
    return min(sharpened, CONST_MAX_COLOR_VALUE);  // 64500.0
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2089&quot; data-origin-height=&quot;659&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ch40bq/dJMcagFau0o/ofDZPqYFF9bosIKRvWXMQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ch40bq/dJMcagFau0o/ofDZPqYFF9bosIKRvWXMQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ch40bq/dJMcagFau0o/ofDZPqYFF9bosIKRvWXMQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fch40bq%2FdJMcagFau0o%2FofDZPqYFF9bosIKRvWXMQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2089&quot; height=&quot;659&quot; data-origin-width=&quot;2089&quot; data-origin-height=&quot;659&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;4.-%EC%BB%A4%EC%8A%A4%ED%85%80-taa-%E2%80%94-history-rejection-%EC%A4%91%EC%8B%AC%EC%9D%98-%EC%A0%91%EA%B7%BC&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 커스텀 TAA &amp;mdash; 대역폭 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MotionMask 방식을 도입하면서 TAA Pass 자체는 경량화되었지만, 기존 MotionVectorsPass에서 MotionVector RT(R16G16_SFloat), MotionVectorDepth RT(D24S8)에서 추가로 MotionMask(R8)가 추가되어 총 3장의 MRT를 사용하게 되고, TAA Pass에서 MotionMask 현재 &amp;amp; 이전 RT 총 2번의 샘플링이 추가되어 대역폭 부담이 매우 커진 상황이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 커스텀 TAA 파이프라인에서는 전반적인 대역폭 최적화를 위해 여러 기법을 도입하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2416&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bleQtT/dJMcab4TxUX/IhnbjWEQbktYbquKpxKYY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bleQtT/dJMcab4TxUX/IhnbjWEQbktYbquKpxKYY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bleQtT/dJMcab4TxUX/IhnbjWEQbktYbquKpxKYY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbleQtT%2FdJMcab4TxUX%2FIhnbjWEQbktYbquKpxKYY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2416&quot; height=&quot;858&quot; data-origin-width=&quot;2416&quot; data-origin-height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1.6-sharpening&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;MotionVector RG8 인코딩&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;URP가&lt;span&gt;&amp;nbsp;&lt;/span&gt;R16G16_SFloat(4바이트/픽셀)를 사용하는 것과 달리,&lt;span&gt;&amp;nbsp;&lt;/span&gt;R8G8_UNorm(2바이트/픽셀)을 채택했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;8비트 정밀도로 Motion Vector를 표현하기 위해 비선형 인코딩을 설계했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;타일 메모리 기준 계산&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URP MotionVector : &lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;MV(R16G16, 4B) + Depth(D24S8, 4B) = 8B/pixel&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;커스텀 MotionVector : &lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;MV(RG8, 2B) + MotionMask(R8, 1B) + Depth(D24S8, 4B) = 7B/pixel&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1776078762724&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 실제 코드: MotionVectorsCommon.hlsl
float2 EncodeMotionVectors(float2 velocity)
{
    float2 compressed = sqrt(abs(velocity));              // 제곱근 압축
    float2 encoded = compressed * sign(velocity) * 0.5 + CONST_ENCODE_BITOFFSET;
    return encoded;  // CONST_ENCODE_BITOFFSET = 0.498039216 (127/255)
}

float2 DecodeMotionVectors(float2 encoded)
{
    float2 shifted = (encoded - CONST_ENCODE_BITOFFSET) * 2.0;
    return sign(shifted) * (abs(shifted) * abs(shifted)); // 제곱 복원
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인코딩 코드 해석&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sqrt :&lt;/b&gt; 16비트 정밀도에서 8비트로 줄이면서, 낮아진 정밀도를 조금이라도 개선하기 위해 sqrt 압축을 통해 &lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;모든 범위를 균등하게 할당하는 대신&lt;/span&gt;&lt;span style=&quot;color: #2b2b2f; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;작은 velocity에 더 많은 비트를 집중&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;시킨다. 제곱근 함수는 0 근처에서 기울기가 급격하므로, 서브픽셀~수 픽셀 범위의 작은 motion에 대해 선형 인코딩 대비 더 세밀한 표현이 가능하다. TAA에서 가장 중요한 것은 작은 motion의 정밀한 reprojection이므로, 큰 motion에서의 정밀도 손실은 허용 가능한 트레이드오프다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;127/255 = 0.498039216 오프셋&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;&lt;b&gt;:&lt;/b&gt; zero velocity가 정확히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;(127, 127)&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;에 매핑되어, 정지 상태의 MV가 8비트 양자화에 의해 미세하게 편향되는 것을 방지한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1.6-sharpening&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;MotionVectorDepth - WriteAll Memoryless 처리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MotionVectorPass 과정에서 ZTest 처리를 위한 MotionVectorDepth RT가 따로 사용된다. 기본적으로 MotionVectorDepth RT는 별도로 사용되는 곳이 없지만, 기본 CameraDepthAttachment를 사용하는 경우, MRT로 묶여있던 CameraColorAttachment까지 MotionVectorPass에서 로드되기 때문에 별도의 MotionVectorDepth를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;229&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;커스텀 MotionVectorPass에서는 MotionVectorDepth RT가 MV 렌더링 패스 내에서만 사용되고 이후 패스에서는 참조되지 않는다. 이에 따라 RenderGraph RasterPass의 AccessFlags를 WriteAll로 설정하고 GlobalTexture로 등록하지 않도록 구성하여, 해당 리소스가 transient로 처리되도록 했다. 이를 통해 Memoryless 최적화를 적용했다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;294&quot; data-start=&quot;231&quot; data-ke-size=&quot;size16&quot;&gt;단, 이 최적화는 Metal API에서만 지원되며, Vulkan에서는 아직 Memoryless를 지원하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/21s1c/dJMb99MKDIa/HztCGtkklD84lQKvxDaB0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/21s1c/dJMb99MKDIa/HztCGtkklD84lQKvxDaB0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/21s1c/dJMb99MKDIa/HztCGtkklD84lQKvxDaB0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F21s1c%2FdJMb99MKDIa%2FHztCGtkklD84lQKvxDaB0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1028&quot; height=&quot;288&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;렌더 스케일만한 D24S8 RT가 생성되는게 메모리에 부담스러워서 CameraDepthAttachment를 사용하도록 테스트해봤지만, 오히려 대역폭 사용량이 증가되는 결과가 확인되어, 모바일 환경에서는 메모리를 좀 사용하더라도 대역폭 최적화가 더 중요하기 때문에 MotionVectorDepth를 사용하기로 결정했다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2445&quot; data-origin-height=&quot;761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GThgY/dJMb9965vKW/QW6dSO9SfYxcY2H67d0fpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GThgY/dJMb9965vKW/QW6dSO9SfYxcY2H67d0fpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GThgY/dJMb9965vKW/QW6dSO9SfYxcY2H67d0fpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGThgY%2FdJMb9965vKW%2FQW6dSO9SfYxcY2H67d0fpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2445&quot; height=&quot;761&quot; data-origin-width=&quot;2445&quot; data-origin-height=&quot;761&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1.6-sharpening&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;TAA CopyPass 제거 (조건부)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP TAA는 항상 TAA Pass 이후, TAA CopyPass를 수행하여 TAA 결과를 복제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 TAA Pass 이후에 Color 버퍼가 수정되는 경우, 다음 프레임에 TAA 히스토리 누적에 오류가 생기기 때문에 그렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 TAA는 TAA Pass 이후에 Color 버퍼를 대상으로 동작하는 RenderPass를 감지하여 CopyPass가 필요 없는 경우, 동작하지 않도록 구현 및 프로젝트 렌더파이프라인 정책으로 필요한 경우가 아니면 TAA Pass 이후에 동작되는 RenderPass가 없도록 가이드했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot;&gt;이 최적화가 적용되는 경우, FHD 기준 약 8.3MB(R11G11B10F)의 풀스크린 복사 대역폭을 절약할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776080876707&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// asmref으로 UniversalRenderer : ScriptableRenderer 내부 메소드 생성.
public bool ExistsPassBetweenExclusive(RenderPassEvent start, RenderPassEvent end)
{
    for (int i = 0; i &amp;lt; m_ActiveRenderPassQueue.Count; i++)
    {
        if (m_ActiveRenderPassQueue[i].renderPassEvent &amp;gt; start &amp;amp;&amp;amp;
            m_ActiveRenderPassQueue[i].renderPassEvent &amp;lt; end)
            return true;
    }
    return false;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;4.-%EC%BB%A4%EC%8A%A4%ED%85%80-taa-%E2%80%94-history-rejection-%EC%A4%91%EC%8B%AC%EC%9D%98-%EC%A0%91%EA%B7%BC&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 성능 측정 및 비주얼 결과&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성능 측정 환경&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;iPhone12 Pro Max (2778x1284)&lt;/li&gt;
&lt;li&gt;Xcode GPU Framecapture&lt;/li&gt;
&lt;/ul&gt;
&lt;table id=&quot;33e75b06-aff0-8073-98ad-dea13d8f66e3&quot; style=&quot;border-collapse: collapse; width: 100%; height: 137px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt;RenderScale / Option&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt; Total GPU Time&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt; MemoryTotal &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt;FS ALU Instructions &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;b&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Device Memory R/W&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33e75b06-aff0-808a-b709-f4a9e505fa43&quot; style=&quot;height: 18px;&quot;&gt;
&lt;td id=&quot;?GAf&quot; style=&quot;height: 18px;&quot;&gt;&lt;b&gt;0.6 / NoAA&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;TkDW&quot; style=&quot;height: 18px; text-align: center;&quot;&gt;1.72 ms&lt;/td&gt;
&lt;td id=&quot;J~|T&quot; style=&quot;height: 18px; text-align: center;&quot;&gt;138.79 MB&lt;/td&gt;
&lt;td id=&quot;@]j{&quot; style=&quot;height: 18px; text-align: center;&quot;&gt;536,973,056&lt;/td&gt;
&lt;td id=&quot;CnsP&quot; style=&quot;height: 18px; text-align: center;&quot;&gt;34.19 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33e75b06-aff0-809e-803c-c99d571a1466&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;?GAf&quot; style=&quot;height: 17px;&quot;&gt;&lt;b&gt;0.6 / FXAA&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;TkDW&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;2.26 ms&lt;/td&gt;
&lt;td id=&quot;J~|T&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;149.89 MB&lt;/td&gt;
&lt;td id=&quot;@]j{&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;463,215,616&lt;/td&gt;
&lt;td id=&quot;CnsP&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;41.03 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33e75b06-aff0-80f8-9189-d3da75471024&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;?GAf&quot; style=&quot;height: 17px;&quot;&gt;&lt;b&gt;0.6 / URPTAA - Low&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;TkDW&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;3.41 ms&lt;/td&gt;
&lt;td id=&quot;J~|T&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;158.57 MB&lt;/td&gt;
&lt;td id=&quot;@]j{&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;1,014,621,952&lt;/td&gt;
&lt;td id=&quot;CnsP&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;60.61 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33e75b06-aff0-8035-8296-cc078a04c18f&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;?GAf&quot; style=&quot;height: 17px;&quot;&gt;&lt;b&gt;0.6 / URPTAA - High&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;TkDW&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;3.97 ms&lt;/td&gt;
&lt;td id=&quot;J~|T&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;158.57 MB&lt;/td&gt;
&lt;td id=&quot;@]j{&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;1,402,311,168&lt;/td&gt;
&lt;td id=&quot;CnsP&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;60.85 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33e75b06-aff0-804c-b516-eda5d3ba5431&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;?GAf&quot; style=&quot;height: 17px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0.6 / CustomTAA - Mobile&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;TkDW&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2.61 ms&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;J~|T&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;151.98 MB&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;@]j{&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;777,932,800&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;CnsP&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;51.46 MiB&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33e75b06-aff0-80ae-9345-f29f328441a6&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;?GAf&quot; style=&quot;height: 17px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;0.6 / CustomTAA - High&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;TkDW&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2.99 ms&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;J~|T&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;151.98 MB&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;@]j{&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1,059,408,640&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td id=&quot;CnsP&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;51.63 MiB&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33e75b06-aff0-8090-a96d-d65d814fc114&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;?GAf&quot; style=&quot;height: 17px;&quot;&gt;&lt;b&gt;1.0 / NoAA&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;TkDW&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;3.41 ms&lt;/td&gt;
&lt;td id=&quot;J~|T&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;176.19 MB&lt;/td&gt;
&lt;td id=&quot;@]j{&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;834,619,904&lt;/td&gt;
&lt;td id=&quot;CnsP&quot; style=&quot;height: 17px; text-align: center;&quot;&gt;60.15 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0.6 / NoAA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzwiEI/dJMcaakCyco/GnlZJZHlICuyU0bAr9gD61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzwiEI/dJMcaakCyco/GnlZJZHlICuyU0bAr9gD61/img.png&quot; data-origin-width=&quot;2423&quot; data-origin-height=&quot;1217&quot; data-is-animation=&quot;false&quot; style=&quot;width: 66.1926%; margin-right: 10px;&quot; data-widthpercent=&quot;66.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzwiEI/dJMcaakCyco/GnlZJZHlICuyU0bAr9gD61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzwiEI%2FdJMcaakCyco%2FGnlZJZHlICuyU0bAr9gD61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2423&quot; height=&quot;1217&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kAtYM/dJMcaiJGmWO/GkCbRBjeE4cHyDKlX7XX7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kAtYM/dJMcaiJGmWO/GkCbRBjeE4cHyDKlX7XX7k/img.png&quot; data-origin-width=&quot;1139&quot; data-origin-height=&quot;1160&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.6447%;&quot; data-widthpercent=&quot;33.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kAtYM/dJMcaiJGmWO/GkCbRBjeE4cHyDKlX7XX7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkAtYM%2FdJMcaiJGmWO%2FGkCbRBjeE4cHyDKlX7XX7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1139&quot; height=&quot;1160&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0.6 / FXAA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brFijC/dJMcadhlhHy/3UTJv0RttgRxd3NcmgwHak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brFijC/dJMcadhlhHy/3UTJv0RttgRxd3NcmgwHak/img.png&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1219&quot; data-is-animation=&quot;false&quot; style=&quot;width: 64.9524%; margin-right: 10px;&quot; data-widthpercent=&quot;65.72&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brFijC/dJMcadhlhHy/3UTJv0RttgRxd3NcmgwHak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrFijC%2FdJMcadhlhHy%2F3UTJv0RttgRxd3NcmgwHak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2436&quot; height=&quot;1219&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mhaCt/dJMcaa50mos/YIYkE6V9mnpteGLEWVNqc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mhaCt/dJMcaa50mos/YIYkE6V9mnpteGLEWVNqc1/img.png&quot; data-origin-width=&quot;1275&quot; data-origin-height=&quot;1223&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.8848%;&quot; data-widthpercent=&quot;34.28&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mhaCt/dJMcaa50mos/YIYkE6V9mnpteGLEWVNqc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmhaCt%2FdJMcaa50mos%2FYIYkE6V9mnpteGLEWVNqc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1275&quot; height=&quot;1223&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0.6 / TAA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMzEFY/dJMcabX8Oga/fbwdQfHjHPcovJuU3OkEJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMzEFY/dJMcabX8Oga/fbwdQfHjHPcovJuU3OkEJ1/img.png&quot; data-origin-width=&quot;2430&quot; data-origin-height=&quot;1207&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;68.42&quot; style=&quot;width: 67.623%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMzEFY/dJMcabX8Oga/fbwdQfHjHPcovJuU3OkEJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMzEFY%2FdJMcabX8Oga%2FfbwdQfHjHPcovJuU3OkEJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2430&quot; height=&quot;1207&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m1eJV/dJMcagFawQ9/KNwgujxCmEAvuLpPr5jYP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m1eJV/dJMcagFawQ9/KNwgujxCmEAvuLpPr5jYP1/img.png&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;1075&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.2142%;&quot; data-widthpercent=&quot;31.58&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m1eJV/dJMcagFawQ9/KNwgujxCmEAvuLpPr5jYP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm1eJV%2FdJMcagFawQ9%2FKNwgujxCmEAvuLpPr5jYP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;999&quot; height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0.6 / NoAA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y8uPx/dJMcagd5jjZ/pk4UtvRNNFEhdhPdvhhCRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y8uPx/dJMcagd5jjZ/pk4UtvRNNFEhdhPdvhhCRK/img.png&quot; data-origin-width=&quot;2425&quot; data-origin-height=&quot;1202&quot; data-is-animation=&quot;false&quot; style=&quot;width: 65.4179%; margin-right: 10px;&quot; data-widthpercent=&quot;66.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y8uPx/dJMcagd5jjZ/pk4UtvRNNFEhdhPdvhhCRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy8uPx%2FdJMcagd5jjZ%2Fpk4UtvRNNFEhdhPdvhhCRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2425&quot; height=&quot;1202&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPiOdb/dJMcacCKZch/qrNVudgtBIYMF6Sy4GMIq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPiOdb/dJMcacCKZch/qrNVudgtBIYMF6Sy4GMIq1/img.png&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;979&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.4193%;&quot; data-widthpercent=&quot;33.81&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPiOdb/dJMcacCKZch/qrNVudgtBIYMF6Sy4GMIq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPiOdb%2FdJMcacCKZch%2FqrNVudgtBIYMF6Sy4GMIq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1009&quot; height=&quot;979&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0.6 / FXAA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F1JjC/dJMcafM166w/5jkkN7UInKhs1JcEpn6dS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F1JjC/dJMcafM166w/5jkkN7UInKhs1JcEpn6dS0/img.png&quot; data-origin-width=&quot;2433&quot; data-origin-height=&quot;1215&quot; data-is-animation=&quot;false&quot; style=&quot;width: 66.6034%; margin-right: 10px;&quot; data-widthpercent=&quot;67.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F1JjC/dJMcafM166w/5jkkN7UInKhs1JcEpn6dS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF1JjC%2FdJMcafM166w%2F5jkkN7UInKhs1JcEpn6dS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2433&quot; height=&quot;1215&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAnLkt/dJMcaduOGRv/gU1iKhMYezDc6vL9TgeTI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAnLkt/dJMcaduOGRv/gU1iKhMYezDc6vL9TgeTI1/img.png&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;1069&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.2339%;&quot; data-widthpercent=&quot;32.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAnLkt/dJMcaduOGRv/gU1iKhMYezDc6vL9TgeTI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAnLkt%2FdJMcaduOGRv%2FgU1iKhMYezDc6vL9TgeTI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1036&quot; height=&quot;1069&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0.6 / TAA&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2csMn/dJMcadav4xb/eKU5xM9KRn3i4oobhrIFIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2csMn/dJMcadav4xb/eKU5xM9KRn3i4oobhrIFIK/img.png&quot; data-origin-width=&quot;2433&quot; data-origin-height=&quot;1218&quot; data-is-animation=&quot;false&quot; style=&quot;width: 65.9273%; margin-right: 10px;&quot; data-widthpercent=&quot;66.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2csMn/dJMcadav4xb/eKU5xM9KRn3i4oobhrIFIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2csMn%2FdJMcadav4xb%2FeKU5xM9KRn3i4oobhrIFIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2433&quot; height=&quot;1218&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mpWzc/dJMcajaK0sY/aKWhHLke2FMetLUzpFzWvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mpWzc/dJMcajaK0sY/aKWhHLke2FMetLUzpFzWvK/img.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;1049&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.9099%;&quot; data-widthpercent=&quot;33.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mpWzc/dJMcajaK0sY/aKWhHLke2FMetLUzpFzWvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmpWzc%2FdJMcajaK0sY%2FaKWhHLke2FMetLUzpFzWvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1046&quot; height=&quot;1049&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>rendergraph</category>
      <category>taa</category>
      <category>Temporal AA</category>
      <category>unity</category>
      <category>URP</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/198</guid>
      <comments>https://darkcatgame.tistory.com/198#entry198comment</comments>
      <pubDate>Mon, 13 Apr 2026 21:42:01 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code Skill for Unity URP Development &amp;mdash; RenderGraph &amp;amp; RendererFeature Guide</title>
      <link>https://darkcatgame.tistory.com/197</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/claude-skill-unity-urp.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/CatDarkGame/claude-skill-unity-urp.git&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775131186280&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - CatDarkGame/claude-skill-unity-urp&quot; data-og-description=&quot;Contribute to CatDarkGame/claude-skill-unity-urp development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/CatDarkGame/claude-skill-unity-urp.git&quot; data-og-url=&quot;https://github.com/CatDarkGame/claude-skill-unity-urp&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dWelgE/dJMb9efekAe/ozu4nu6lXhAtKOc1u9rcG1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Ytv6u/dJMb9gxlMm2/2eZlUe5SDPniBJBKRzyLiK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bTGDbw/dJMb9kmc8Fm/ZyTYWm0qjHAyid0LE1sr2k/img.png?width=1229&amp;amp;height=671&amp;amp;face=0_0_1229_671&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/claude-skill-unity-urp.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/CatDarkGame/claude-skill-unity-urp.git&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dWelgE/dJMb9efekAe/ozu4nu6lXhAtKOc1u9rcG1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Ytv6u/dJMb9gxlMm2/2eZlUe5SDPniBJBKRzyLiK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bTGDbw/dJMb9kmc8Fm/ZyTYWm0qjHAyid0LE1sr2k/img.png?width=1229&amp;amp;height=671&amp;amp;face=0_0_1229_671');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - CatDarkGame/claude-skill-unity-urp&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to CatDarkGame/claude-skill-unity-urp development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unity-urp-dev는&amp;nbsp;Unity&amp;nbsp;URP&amp;nbsp;RenderGraph&amp;nbsp;기반&amp;nbsp;렌더피쳐&amp;nbsp;개발&amp;nbsp;시&amp;nbsp;Claude&amp;nbsp;Code가&amp;nbsp;올바른&amp;nbsp;방향으로&amp;nbsp;코드를&amp;nbsp;생성하도록&amp;nbsp;안내하는&amp;nbsp;스킬입니다. &lt;br /&gt;&lt;br /&gt;이&amp;nbsp;스킬의&amp;nbsp;핵심은&amp;nbsp;두&amp;nbsp;가지입니다. &lt;br /&gt;&lt;br /&gt;1.&amp;nbsp;고급&amp;nbsp;렌더링&amp;nbsp;기법&amp;nbsp;레퍼런스 &lt;br /&gt;references/&amp;nbsp;폴더에는&amp;nbsp;RenderGraph&amp;nbsp;API&amp;nbsp;패턴,&amp;nbsp;Job&amp;nbsp;System&amp;nbsp;/&amp;nbsp;Burst&amp;nbsp;컴파일러,&amp;nbsp;Compute&amp;nbsp;Shader,&amp;nbsp;unsafe&amp;nbsp;zero-copy&amp;nbsp;메모리&amp;nbsp;전송&amp;nbsp;등&amp;nbsp;실전&amp;nbsp;구현에&amp;nbsp;필요한&amp;nbsp;고급&amp;nbsp;기법이&amp;nbsp;문서화되어&amp;nbsp;있습니다.&amp;nbsp;Claude&amp;nbsp;Code가&amp;nbsp;이&amp;nbsp;레퍼런스를&amp;nbsp;직접&amp;nbsp;읽고&amp;nbsp;검증된&amp;nbsp;패턴으로&amp;nbsp;코드를&amp;nbsp;작성합니다. &lt;br /&gt;&lt;br /&gt;2.&amp;nbsp;URP&amp;nbsp;버전&amp;nbsp;인식&amp;nbsp;코드&amp;nbsp;생성 &lt;br /&gt;스킬은&amp;nbsp;작업&amp;nbsp;시작&amp;nbsp;전&amp;nbsp;프로젝트의&amp;nbsp;URP&amp;nbsp;버전을&amp;nbsp;자동으로&amp;nbsp;감지합니다.&amp;nbsp;감지된&amp;nbsp;버전에&amp;nbsp;맞는&amp;nbsp;공식&amp;nbsp;Unity&amp;nbsp;Docs와&amp;nbsp;URP&amp;nbsp;GitHub&amp;nbsp;소스를&amp;nbsp;참조하여,&amp;nbsp;버전별&amp;nbsp;API&amp;nbsp;차이로&amp;nbsp;인한&amp;nbsp;오류를&amp;nbsp;사전에&amp;nbsp;방지합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트 샘플&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 스킬을 활용해서 SSR, RadialBlur을 최소한의 자연어 프롬프트로 제작했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AUg6r/dJMcagydOwv/GKvDpkLQutXMyes7cRbDlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AUg6r/dJMcagydOwv/GKvDpkLQutXMyes7cRbDlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AUg6r/dJMcagydOwv/GKvDpkLQutXMyes7cRbDlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAUg6r%2FdJMcagydOwv%2FGKvDpkLQutXMyes7cRbDlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1237&quot; height=&quot;678&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1248&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dudeIg/dJMcaflN5Qy/OzmFIZcbYxX0dmjsUKEeB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dudeIg/dJMcaflN5Qy/OzmFIZcbYxX0dmjsUKEeB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dudeIg/dJMcaflN5Qy/OzmFIZcbYxX0dmjsUKEeB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdudeIg%2FdJMcaflN5Qy%2FOzmFIZcbYxX0dmjsUKEeB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1248&quot; height=&quot;694&quot; data-origin-width=&quot;1248&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ume3P/dJMcadhc9Kx/ngbt3i3RGaMzkHkQAJLcg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ume3P/dJMcadhc9Kx/ngbt3i3RGaMzkHkQAJLcg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ume3P/dJMcadhc9Kx/ngbt3i3RGaMzkHkQAJLcg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fume3P%2FdJMcadhc9Kx%2Fngbt3i3RGaMzkHkQAJLcg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1162&quot; height=&quot;409&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Misc</category>
      <category>claude code</category>
      <category>RenderGraph SKill</category>
      <category>unity</category>
      <category>Unity Skills</category>
      <category>Unity URP Claude code</category>
      <category>unity6</category>
      <category>URP</category>
      <category>URP Skill</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/197</guid>
      <comments>https://darkcatgame.tistory.com/197#entry197comment</comments>
      <pubDate>Thu, 2 Apr 2026 21:00:35 +0900</pubDate>
    </item>
    <item>
      <title>릴스 광고 수집 데이터셋 시스템</title>
      <link>https://darkcatgame.tistory.com/196</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;UA&amp;nbsp;소재&amp;nbsp;트렌드&amp;nbsp;파악을&amp;nbsp;위해&amp;nbsp;인스타&amp;nbsp;릴스&amp;nbsp;광고를&amp;nbsp;꾸준히&amp;nbsp;모니터링하는데, &lt;br /&gt;레퍼런스 저장하는 과정이 번거로워서 자동화했습니다. &lt;br /&gt;&lt;br /&gt;DM으로&amp;nbsp;릴스&amp;nbsp;공유&amp;nbsp;&amp;rarr;&amp;nbsp;자동&amp;nbsp;다운로드&amp;nbsp;+&amp;nbsp;분류&amp;nbsp;&amp;rarr;&amp;nbsp;Notion&amp;nbsp;DB&amp;nbsp;적재까지&amp;nbsp;원클릭. &lt;br /&gt;&lt;br /&gt;AI를 활용해 Chrome Extension + Python 서버 구조로 만들었고, 오픈소스로 공개했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/reels-catcher-extension.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/CatDarkGame/reels-catcher-extension.git&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2538&quot; data-origin-height=&quot;876&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXAdJJ/dJMcaa5R5FZ/J8pChHIyI19y9vuOOTEYF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXAdJJ/dJMcaa5R5FZ/J8pChHIyI19y9vuOOTEYF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXAdJJ/dJMcaa5R5FZ/J8pChHIyI19y9vuOOTEYF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXAdJJ%2FdJMcaa5R5FZ%2FJ8pChHIyI19y9vuOOTEYF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2538&quot; height=&quot;876&quot; data-origin-width=&quot;2538&quot; data-origin-height=&quot;876&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;996&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ReUJm/dJMcabDH7G6/puNN0kqvoCVfJrIrQWHOQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ReUJm/dJMcabDH7G6/puNN0kqvoCVfJrIrQWHOQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ReUJm/dJMcabDH7G6/puNN0kqvoCVfJrIrQWHOQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FReUJm%2FdJMcabDH7G6%2FpuNN0kqvoCVfJrIrQWHOQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;753&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;996&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Misc</category>
      <category>GameMarketing</category>
      <category>MobileGaming</category>
      <category>UA</category>
      <category>UserAcquisition</category>
      <category>릴스 광고</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/196</guid>
      <comments>https://darkcatgame.tistory.com/196#entry196comment</comments>
      <pubDate>Thu, 2 Apr 2026 19:58:12 +0900</pubDate>
    </item>
    <item>
      <title>Unity PostProcess 최적화 - Mipmap Bloom</title>
      <link>https://darkcatgame.tistory.com/195</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cobKdR/dJMcaiJpDIp/nIkOWSFyHJpWkx8fxsANYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cobKdR/dJMcaiJpDIp/nIkOWSFyHJpWkx8fxsANYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cobKdR/dJMcaiJpDIp/nIkOWSFyHJpWkx8fxsANYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcobKdR%2FdJMcaiJpDIp%2FnIkOWSFyHJpWkx8fxsANYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;981&quot; height=&quot;884&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;884&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;bloom-%EC%86%8C%EA%B0%9C&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Bloom 소개&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bloom은 밝은 영역에서 빛이 번지는 효과를 시뮬레이션하는 포스트 프로세싱이다. HDR 렌더링 환경에서 시각적 품질을 크게 끌어올리는 핵심 이펙트로, 현대 게임에서 거의 필수적으로 사용된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 Bloom의 처리 흐름은 다음과 같다:&lt;/p&gt;
&lt;pre class=&quot;isbl&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;Camera Color &amp;rarr; Prefilter(밝기 추출) &amp;rarr; DownSample(반복 축소) &amp;rarr; Blur(블러) &amp;rarr; UpSample(반복 확대) &amp;rarr; Composite(합성)

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;밝은 픽셀을 추출하고, 해상도를 단계적으로 낮추며 블러를 적용한 뒤, 다시 단계적으로 원본 해상도까지 합성해 올라가는 구조다. Unity URP의 기본 Bloom도 이 흐름을 따르며, Progressive DownSample/UpSample 방식으로 최대 16개의 렌더 패스를 사용한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%EB%AA%A8%EB%B0%94%EC%9D%BC%EC%97%90%EC%84%9C%EC%9D%98-%EB%AC%B8%EC%A0%9C%3A-setrendertarget%EC%9D%98-%EC%8B%A4%EC%A0%9C-%EB%B9%84%EC%9A%A9&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;모바일에서의 문제: SetRenderTarget의 실제 비용&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bloom은 화면 전체를 대상으로 하는 풀스크린 이펙트다. 해상도에 비례해 비용이 증가하고, 여러 단계의 DownSample/UpSample 과정에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;매 패스마다&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetRenderTarget(SRT)이 호출&lt;/b&gt;된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;setrendertarget%EC%9D%B4%EB%9E%80&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;SetRenderTarget이란&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SetRenderTarget은 GPU가 렌더링 결과를 기록할 대상 텍스처를 전환하는 명령이다. CPU 관점에서는 단순한 상태 변경처럼 보이지만, GPU 내부에서는 훨씬 복잡한 동작이 일어난다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;tbdr-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%97%90%EC%84%9C-srt%EC%9D%98-%EB%8F%99%EC%9E%91&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;TBDR 아키텍처에서 SRT의 동작&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모바일 GPU(Apple GPU, Mali, Adreno 등)는 Tile-Based Deferred Rendering(TBDR) 아키텍처를 사용한다. TBDR에서 렌더링은 화면을 작은 타일(보통 16&amp;times;16 ~ 32&amp;times;32 픽셀)로 분할하여 처리한다. 각 타일은 GPU 코어 내부의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;타일 메모리(On-Chip Memory)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 렌더링되며, 이 메모리는 시스템 메모리(DRAM)보다 수십 배 빠르고 전력 소모가 적다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SRT가 호출되면 GPU는 다음 과정을 수행한다:&lt;/p&gt;
&lt;pre class=&quot;less&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;[현재 렌더 타겟의 타일 메모리]
        &amp;darr; Store (On-Chip &amp;rarr; DRAM)
[시스템 메모리에 기록]
        &amp;darr; 렌더 타겟 전환
[새 렌더 타겟의 이전 내용]
        &amp;darr; Load (DRAM &amp;rarr; On-Chip)  
[새 렌더 타겟의 타일 메모리에서 렌더링 시작]

&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;Store&lt;/b&gt;: 현재 렌더 타겟의 타일 메모리 내용을 시스템 메모리(DRAM)로 기록한다&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;Load&lt;/b&gt;: 새 렌더 타겟의 기존 내용을 시스템 메모리에서 타일 메모리로 읽어온다&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 Store/Load 사이클이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;디바이스 메모리 대역폭을 소비하는 핵심 원인&lt;/b&gt;이다. 타일 메모리 내에서의 읽기/쓰기는 거의 무비용이지만, DRAM과의 데이터 전송은 높은 전력을 소모하고 대역폭 병목을 유발한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;%EC%99%9C-%EB%8C%80%EC%97%AD%ED%8F%AD%EC%9D%B4-%EC%A4%91%EC%9A%94%ED%95%9C%EA%B0%80&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;왜 대역폭이 중요한가&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데스크톱 GPU에서는 대역폭에 여유가 있어 SRT 전환의 비용이 상대적으로 작다. 그러나 모바일에서는 사정이 다르다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;전력 제한&lt;/b&gt;: DRAM 접근은 ALU 연산 대비 수십 배의 에너지를 소모한다. 대역폭 증가는 곧 발열과 배터리 소모 증가를 의미한다&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;서멀 스로틀링&lt;/b&gt;: 발열이 임계값을 넘으면 GPU 클럭이 강제로 낮아져 전체 프레임 레이트가 떨어진다&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;공유 대역폭&lt;/b&gt;: 모바일 SoC에서 CPU와 GPU는 동일한 시스템 메모리와 대역폭을 공유한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;URP 기본 Bloom은 DownSample과 UpSample을 합쳐 최대&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;16번의 SRT 호출&lt;/b&gt;이 발생한다. 매 호출마다 Store &amp;rarr; Load 사이클이 반복되며, 이 과정에서 소비되는 대역폭은 프레임당 누적된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;bloom-%EC%B5%9C%EC%A0%81%ED%99%94%EA%B0%80-%EC%96%B4%EB%A0%A4%EC%9A%B4-%EC%9D%B4%EC%9C%A0&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Bloom 최적화가 어려운 이유&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bloom 최적화를 논의할 때 가장 먼저 떠오르는 접근은 블러 알고리즘 자체를 변경하는 것이다. Dual Filter, Kawase Blur 등이 대표적이다. 이들은 적은 샘플 수로 넓은 범위의 블러를 근사하여 연산 비용을 줄인다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 실제 프로젝트에서 이 접근을 채택하기는 쉽지 않다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;품질 차이.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;URP 기본 Bloom은 Progressive Gaussian 기반의 블러를 사용하며, 이미 검증된 시각적 품질을 제공한다. Dual Filter나 Kawase Blur로 전환하면 블러의 특성(falloff, 형태)이 달라지고, 특히 밝은 광원 주변의 번짐 패턴이 눈에 띄게 변한다. 아트 디렉션 관점에서 이 차이를 수용하기 어려운 경우가 많다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 전반에 미치는 영향.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bloom은 게임의 전체 비주얼 톤을 결정하는 요소 중 하나다. 이미 Bloom 기반으로 라이팅, 이펙트, UI 연출이 세팅된 프로젝트에서 블러 알고리즘을 변경하면 모든 씬의 룩이 달라진다. 프로젝트 초반이 아니라면 사실상 선택지에서 제외된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결국 필요한 것은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;기존과 동일한 블러 품질을 유지하면서, 렌더링 파이프라인 레벨에서 비용을 줄이는 접근&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2RC1S/dJMcafspOHa/oHOKeHdBTzbjHfl07S7Td0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2RC1S/dJMcafspOHa/oHOKeHdBTzbjHfl07S7Td0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2RC1S/dJMcafspOHa/oHOKeHdBTzbjHfl07S7Td0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2RC1S%2FdJMcafspOHa%2FoHOKeHdBTzbjHfl07S7Td0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1694&quot; height=&quot;517&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;mipmap-bloom-%EC%A0%91%EA%B7%BC&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Mipmap Bloom 접근&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Mipmap Bloom의 핵심 아이디어는 단순하다:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;여러 해상도의 블러 결과를 별도의 렌더 타겟으로 분리하지 않고, 하나의 렌더 타겟 안에 영역을 나눠 배치&lt;/b&gt;하는 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 기법의 이름을 &quot;Mipmap Bloom&quot;이라 했지만, 엄밀히 말하면 GPU의 하드웨어 Mipmap(텍스처 LOD 체인)을 사용하는 것은 아니다. 실제 동작은 여러 해상도의 블러 결과를 &quot;하나의 렌더 타겟에 아틀라스&quot;처럼 패킹하는 방식이므로, &quot;Atlas Bloom&quot;이 더 정확한 표현이다. 다만 각 해상도 단계가 점진적으로 축소되는 구조가 Mipmap 체인과 개념적으로 유사하고, DownSample로 만든 여러 해상도 레벨을 다루는 패스를 코드에서 &quot;MipmapBlur&quot;로 명명했기 때문에 Mipmap Bloom이라는 이름을 그대로 사용한다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TEBEn/dJMcaiJpDcD/JdGOHWPjEAxqDHjVHIizZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TEBEn/dJMcaiJpDcD/JdGOHWPjEAxqDHjVHIizZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TEBEn/dJMcaiJpDcD/JdGOHWPjEAxqDHjVHIizZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTEBEn%2FdJMcaiJpDcD%2FJdGOHWPjEAxqDHjVHIizZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;199&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;urp-bloom-vs-mipmap-bloom-%EA%B5%AC%EC%A1%B0-%EB%B9%84%EA%B5%90&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;URP Bloom vs Mipmap Bloom 구조 비교&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;URP 기본 Bloom (URP 버전마다 차이 있음) :&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;[SRT] DownSample 0  &amp;rarr;  [SRT] DownSample 1  &amp;rarr;  ...  &amp;rarr;  [SRT] DownSample 7
                                                              &amp;darr;
[SRT] UpSample 7    &amp;larr;  [SRT] UpSample 6    &amp;larr;  ...  &amp;larr;  [SRT] UpSample 0

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;총 16개 패스, 16번의 SetRenderTarget 호출&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;매 패스마다 Store &amp;rarr; Load 사이클 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJI4f/dJMcahDKUd9/gtQm4NuenpYypgquBgyNRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJI4f/dJMcahDKUd9/gtQm4NuenpYypgquBgyNRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJI4f/dJMcahDKUd9/gtQm4NuenpYypgquBgyNRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJI4f%2FdJMcahDKUd9%2FgtQm4NuenpYypgquBgyNRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;322&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Mipmap Bloom:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;[SRT] Prefilter &amp;rarr; [SRT] DownSample &amp;times;5 &amp;rarr; [SRT] MipmapBlur H &amp;rarr; [SRT] MipmapBlur V &amp;rarr; [SRT] Composite

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;총 9개 패스, 9번의 SetRenderTarget 호출&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;MipmapBlur H/V는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;하나의 렌더 타겟 안에서&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;4개 Mip Level을&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetViewport로 영역 분할하여 렌더링&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJralD/dJMcaaxTKcV/kdPn8t1t6s4gGreT0v05B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJralD/dJMcaaxTKcV/kdPn8t1t6s4gGreT0v05B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJralD/dJMcaaxTKcV/kdPn8t1t6s4gGreT0v05B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJralD%2FdJMcaaxTKcV%2FkdPn8t1t6s4gGreT0v05B0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;372&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%EC%95%84%ED%8B%80%EB%9D%BC%EC%8A%A4-%EA%B8%B0%EB%B0%98-mipmap-%ED%8C%A8%ED%82%B9&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;아틀라스 기반 Mipmap 패킹&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 MipmapBlur 단계에 있다. 4개 Mip Level의 블러를 각각 별도 RT에 그리는 대신, 하나의 RT를 아틀라스처럼 사용한다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;┌─────────────┬───────┐
│             │       │
│   Mip 0     │ Mip 1 │
│  (가장 큼)   │       │
│             ├───┬───┤
│             │M2 │   │
├─────────────┤───┤M3 │
│             │   │   │
└─────────────┴───┴───┘

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 Mip Level은&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetViewport로 렌더링 영역을 제한하여 같은 RT 내 다른 영역에 그린다. 이렇게 하면:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;MipmapBlur Horizontal&lt;/b&gt;: 1번의 SRT &amp;rarr; 4개 Mip Level을 순차적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetViewport로 영역을 바꿔가며 블러&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;MipmapBlur Vertical&lt;/b&gt;: 1번의 SRT &amp;rarr; 동일하게 4개 Mip Level을 영역별로 블러&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;Composite&lt;/b&gt;: 아틀라스에서 각 Mip Level의 UV 영역을 계산하여 한 번에 샘플링 &amp;amp; 합산&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존에 Mip Level 수 &amp;times; 2(H+V) = 8번의 SRT가 필요했던 블러 단계가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;단 2번의 SRT&lt;/b&gt;로 줄어든다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;urp-bloom-vs-mipmap-bloom-%EA%B5%AC%EC%A1%B0-%EB%B9%84%EA%B5%90&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;URP Bloom vs Mipmap Bloom 비주얼 비교&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5QOFL/dJMb99Z4Pm7/7EHXzdRHwPTvGhYZACHCRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5QOFL/dJMb99Z4Pm7/7EHXzdRHwPTvGhYZACHCRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5QOFL/dJMb99Z4Pm7/7EHXzdRHwPTvGhYZACHCRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5QOFL%2FdJMb99Z4Pm7%2F7EHXzdRHwPTvGhYZACHCRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;981&quot; height=&quot;884&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;884&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;uv-tiling%2Foffset%EA%B3%BC-constant-buffer-%EC%84%A4%EA%B3%84&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;UV Tiling/Offset과 Constant Buffer 설계&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아틀라스 내에서 각 Mip Level의 정확한 위치를 참조하려면 UV 변환 데이터가 필요하다. 이 데이터를 셰이더에 상수로 하드코딩하지 않고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;CPU에서 계산하여 Constant Buffer로 전달하는 이유&lt;/b&gt;가 있다.&lt;/p&gt;
&lt;h4 id=&quot;%EC%99%9C-%EC%85%B0%EC%9D%B4%EB%8D%94-%EC%83%81%EC%88%98%EA%B0%80-%EC%95%84%EB%8B%8C-cpu-%EC%A0%84%EC%86%A1%EC%9D%B8%EA%B0%80&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;왜 셰이더 상수가 아닌 CPU 전송인가&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아틀라스의 UV 레이아웃은 화면 해상도와 품질 설정에 따라 달라진다. Mip Level별 샘플 카운트를 조절하면 블러 품질과 성능 사이의 트레이드오프를 런타임에 제어할 수 있다:&lt;/p&gt;
&lt;pre class=&quot;csharp&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;// 품질 설정에 따라 Mip Level별 샘플 수가 달라진다
private static readonly int[] MipmapSampleCounts      = { 4, 4, 8, 12 };     // Normal
private static readonly int[] MipmapSampleCounts_High = { 8, 12, 16, 32 };   // High Quality&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해상도 변경, 품질 옵션 전환, 디바이스별 설정 분기 등에 대응하려면 이 데이터가 동적이어야 한다. 셰이더에 상수로 고정하면 이 유연성을 잃는다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;burst-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EC%9D%84-%ED%86%B5%ED%95%9C-cpu-%EC%97%B0%EC%82%B0-%EC%B5%9C%EC%A0%81%ED%99%94&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Burst 컴파일을 통한 CPU 연산 최적화&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;UV Tiling/Offset, Clamp 값, Gaussian 블러의 offset/weight는 매 프레임 계산되는 것은 아니지만, 해상도나 설정이 바뀔 때 재계산이 필요하다. 이 연산을 효율적으로 수행하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Unity Burst Compiler를 활용&lt;/b&gt;한다:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;[BurstCompile]
internal static unsafe void GetGaussianUVOffsets(
    float4* refBlurDatas, int startIndex, int sampleCount, int width, int height)
{
    float dx = 1.0f / width;
    float dy = 1.0f / height;
    float half = (sampleCount - 1) * 0.5f;
    for (int i = 0; i &amp;lt; sampleCount; i++)
    {
        float offset = i - half;
        float4 data = refBlurDatas[startIndex + i];
        refBlurDatas[startIndex + i] = new float4(offset * dx, offset * dy, data.z, data.w);
    }
}

[BurstCompile]
internal static unsafe void GetGaussianWeights(
    float4* refBlurDatas, int startIndex, int sampleCount, float sigma)
{
    // Gaussian weight 계산 &amp;mdash; Burst로 네이티브 수준 성능
    float twoSigmaSq = 2f * sigma * sigma;
    float sum = 0f;
    float center = (sampleCount - 1) * 0.5f;
    for (int i = 0; i &amp;lt; sampleCount; i++)
    {
        float x = i - center;
        float w = math.exp(-(x * x) / twoSigmaSq);
        float4 data = refBlurDatas[startIndex + i];
        refBlurDatas[startIndex + i] = new float4(data.x, data.y, w, data.w);
        sum += w;
    }
    // 정규화
    for (int i = 0; i &amp;lt; sampleCount; i++)
    {
        float4 data = refBlurDatas[startIndex + i];
        refBlurDatas[startIndex + i] = new float4(data.x, data.y, data.z / sum, data.w);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Burst 컴파일은 C# 코드를 LLVM 기반 네이티브 코드로 변환하여, NativeArray를 통한 unsafe 포인터 연산을 C/C++ 수준의 성능으로 처리한다. 계산된 모든 데이터(샘플 카운트, blur offset/weight, UV tiling/offset, UV clamp)는 하나의&lt;span&gt;&amp;nbsp;&lt;/span&gt;ComputeBuffer에 패킹되어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;단일&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetGlobalConstantBuffer&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출로 GPU에 전송&lt;/b&gt;된다:&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;cmd.SetGlobalConstantBuffer(
    _mipmapData.ConstantsBuffer, 
    ShaderPropertyId.MipmapDataConstants, 
    0, _mipmapData.ConstantsBufferSize);

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPU 셰이더에서는 이 Constant Buffer를 그대로 참조한다:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;CBUFFER_START(MipmapDataConstants)
    uint4  _BloomMipmapSampleCount;                      // Mip Level별 샘플 수
    float4 _BloomMipmapBlurData[MIPMAP_COUNT * 32u];     // Gaussian offset/weight
    float4 _BloomMipmapTilingOffset[MIPMAP_COUNT];       // UV Tiling &amp;amp; Offset
    float4 _BloomMipmapUVClamp[MIPMAP_COUNT];            // UV Clamp 범위
CBUFFER_END

&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;_BloomMipmapTilingOffset: Mip Level 영역의 UV 스케일과 오프셋. Composite 및 Vertical Blur 패스에서 전체 화면 UV를 해당 Mip 영역의 UV로 변환하는 데 사용한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;_BloomMipmapUVClamp: 텍스처 샘플링 시 인접 Mip 영역의 픽셀을 침범하지 않도록 UV를 제한한다. 아틀라스 패킹의 필수 요소다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 구조의 장점은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;CPU 연산은 Burst로 최적화, GPU 전송은 단일 Constant Buffer로 최소화, 런타임 품질 조절은 유연하게 유지&lt;/b&gt;하는 세 가지를 동시에 달성한다는 점이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;setrendertarget-%EC%A0%9C%EA%B1%B0%EC%9D%98-%ED%9A%A8%EA%B3%BC&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;SetRenderTarget 제거의 효과&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 접근이 효과적인 이유는 앞서 설명한 TBDR 아키텍처의 Store/Load 사이클과 직결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP BloomMipmap Bloom변화&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SetRenderTarget 호출&lt;/td&gt;
&lt;td&gt;16회&lt;/td&gt;
&lt;td&gt;9회&lt;/td&gt;
&lt;td&gt;&lt;b&gt;-43.8%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa;&quot;&gt;
&lt;td&gt;디바이스 메모리 Read&lt;/td&gt;
&lt;td&gt;5.10 MiB&lt;/td&gt;
&lt;td&gt;4.30 MiB&lt;/td&gt;
&lt;td&gt;-15.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;디바이스 메모리 Write&lt;/td&gt;
&lt;td&gt;2.05 MiB&lt;/td&gt;
&lt;td&gt;0.93 MiB&lt;/td&gt;
&lt;td&gt;&lt;b&gt;-54.5%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa;&quot;&gt;
&lt;td&gt;디바이스 메모리 총량 (R+W)&lt;/td&gt;
&lt;td&gt;7.15 MiB&lt;/td&gt;
&lt;td&gt;5.23 MiB&lt;/td&gt;
&lt;td&gt;&lt;b&gt;-26.9%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;렌더 패스 수&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;-43.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;측정 환경: Xcode GPU Frame Capture, iPhone 12 Pro Max 2778x1284&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SRT 호출이 줄어들면 타일 Store/Load 사이클이 줄고, 이는 디바이스 메모리 Write의 54.5% 감소로 직접 확인된다. 모바일에서 메모리 대역폭은 전력 소모의 주요 원인이므로, 이 감소는 동일한 프레임 예산 내에서 다른 렌더링 작업에 더 많은 여유를 확보해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EC%B5%9C%EC%A0%81%ED%99%94%3A-uv-%EC%97%B0%EC%82%B0%EC%9D%84-vertex-stage%EC%97%90%EC%84%9C-%EC%B2%98%EB%A6%AC&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;마이크로 최적화: UV 연산을 Vertex Stage에서 처리&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;풀스크린 포스트 프로세싱에서 흔히 간과되는 최적화가 있다. Fragment Shader에서 수행하는 UV 관련 연산을 Vertex Shader로 옮기는 것이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;풀스크린 삼각형은 화면 전체를 덮는 3개의 정점만 사용한다. Fragment Shader는 화면의 모든 픽셀(수십만~수백만 회)에 대해 호출되지만, Vertex Shader는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;단 3번&lt;/b&gt;만 호출된다. GPU 하드웨어는 Vertex Shader 출력을 Fragment 단계로 넘길 때 자동으로 보간(interpolation)해주므로, 선형 변환(Tiling/Offset 적용 등)은 Vertex에서 처리해도 결과가 동일하다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Composite 패스 예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;// Vertex Shader &amp;mdash; 3회 호출
Varyings FullscreenVert(Attributes input)
{
    // ...
    // 4개 Mip Level의 UV를 미리 계산하여 varying으로 전달
    output.uv_0.xy = uv * _BloomMipmapTilingOffset[0].xy + _BloomMipmapTilingOffset[0].zw;
    output.uv_0.zw = uv * _BloomMipmapTilingOffset[1].xy + _BloomMipmapTilingOffset[1].zw;
    output.uv_1.xy = uv * _BloomMipmapTilingOffset[2].xy + _BloomMipmapTilingOffset[2].zw;
    output.uv_1.zw = uv * _BloomMipmapTilingOffset[3].xy + _BloomMipmapTilingOffset[3].zw;
    return output;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 이 연산을 Fragment Shader에서 수행했다면 해상도에 비례하는 횟수(예: 189,120회)만큼 곱셈-덧셈이 반복된다. Vertex로 옮기면 이 연산은 3회로 줄고, 나머지는 하드웨어 보간기가 처리한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동일한 패턴이 MipmapBlur Vertical 패스에서도 적용된다:&lt;/p&gt;
&lt;pre class=&quot;verilog&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;Varyings FullscreenVert(Attributes input)
{
    // ...
    // Mip Level 영역의 UV Tiling/Offset 적용
    const float4 tilingOffset = _BloomMipmapTilingOffset[_MipmapCount];
    output.uv.xy = output.uv.xy * tilingOffset.xy + tilingOffset.zw;
    // UV Clamp 값도 varying으로 전달
    output.offsetClamp = _BloomMipmapUVClamp[_MipmapCount];
    return output;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bloom 같은 풀스크린 이펙트에서 이 패턴은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Fragment에서 상수 버퍼 접근 + 산술 연산 수십만 회를 Vertex 3회 + 하드웨어 보간&lt;/b&gt;으로 대체하는 것이므로, ALU 부하와 버퍼 대역폭 양쪽 모두에서 이득이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;%EC%84%B1%EB%8A%A5-%EC%B8%A1%EC%A0%95&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;성능 측정&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Xcode GPU Frame Capture를 통해 URP 기본 Bloom과 Mipmap Bloom의 GPU 성능을 비교 측정했다. 주요 지표 두 가지 &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;GPU 연산 비용(Execution Cost, ALU)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;디바이스 메모리 대역폭&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 을 중심으로 분석한다.&lt;/p&gt;
&lt;h3 id=&quot;%EC%B8%A1%EC%A0%95-%ED%99%98%EA%B2%BD&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;측정 환경&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;디바이스: iPhone 12 Pro Max&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;해상도: 2778&amp;times;1284 (Native)&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;측정 도구: Xcode GPU Frame Capture&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;비교 범위: Bloom + UberPostProcess 전체 (포스트 프로세싱 파이프라인 단위)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;%EC%B8%A1%EC%A0%95-%EC%A7%80%ED%91%9C-%EC%84%A4%EB%AA%85&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;측정 지표 설명&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;Total GPU Time&lt;/b&gt;: 해당 구간의 절대 GPU 소요 시간&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;Execution Cost&lt;/b&gt;: 전체 프레임 GPU 시간에서 차지하는 비율&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit;&quot;&gt;&lt;b&gt;FS ALU Instructions&lt;/b&gt;: Fragment Shader에서 실행된 ALU(산술 논리 연산) 명령어 수. 셰이더의 연산 복잡도를 나타낸다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;%EC%A2%85%ED%95%A9-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90-(bloom-%2B-uberpostprocess)&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;종합 성능 비교 (Bloom + UberPostProcess)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지표URP BloomMipmap Bloom차이&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Total GPU Time&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;3.33 ms&lt;/td&gt;
&lt;td&gt;3.01 ms&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&amp;minus;0.32 ms (&amp;minus;9.61%)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa;&quot;&gt;
&lt;td&gt;&lt;b&gt;Execution Cost&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;34.74%&lt;/td&gt;
&lt;td&gt;32.70%&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&amp;minus;5.87%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FS ALU Instructions&lt;/td&gt;
&lt;td&gt;385,469,696&lt;/td&gt;
&lt;td&gt;337,791,232&lt;/td&gt;
&lt;td&gt;&amp;minus;47,678,464 (&amp;minus;12.37%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;%EB%94%94%EB%B0%94%EC%9D%B4%EC%8A%A4-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%8C%80%EC%97%AD%ED%8F%AD-%EB%B9%84%EA%B5%90&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;디바이스 메모리 대역폭 비교&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대역폭 비교에는 전송률(GiB/s)이 아닌&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DRAM 전송 총량(Bytes Read + Written)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 사용한다. 전송률은 패스의 실행 시간에 의존하여 합산 시 의미가 왜곡되지만, 총량은 시간과 무관하게 DRAM에 가한 실제 부담을 보여주며 전력 소모와 직접 비례한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bloom 패스만 비교 (SRT 최적화의 직접 효과):&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분URP Bloom (16 pass)Mipmap Bloom (9 pass)차이&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Device Memory Read&lt;/td&gt;
&lt;td&gt;5.10 MiB&lt;/td&gt;
&lt;td&gt;4.30 MiB&lt;/td&gt;
&lt;td&gt;&amp;minus;0.80 MiB (&amp;minus;15.7%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f5f7fa;&quot;&gt;
&lt;td&gt;Device Memory Write&lt;/td&gt;
&lt;td&gt;2.05 MiB&lt;/td&gt;
&lt;td&gt;0.93 MiB&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&amp;minus;1.12 MiB (&amp;minus;54.5%)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;총 전송량 (R+W)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;7.15 MiB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;5.23 MiB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&amp;minus;1.92 MiB (&amp;minus;26.9%)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Write 감소가 두드러진다. SRT 호출 16회 &amp;rarr; 9회 감소로 타일 Store 횟수가 줄어든 직접적인 결과다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%EB%B6%84%EC%84%9D&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;분석&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GPU Time &amp;minus;9.61%.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Bloom + UberPostProcess 전체 구간에서 0.32ms가 감소했다. 60fps 기준 프레임 예산 16.67ms에서 약 2%에 해당하는 여유를 확보한 셈이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ALU Instructions &amp;minus;12.37%.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;약 4,768만 명령어가 줄었다. Mipmap Bloom은 UpSample 체인(8패스)을 제거하고 Composite 1회로 대체했기 때문에, 해당 구간의 연산이 통째로 사라진다. UberPostProcess에서 MipmapBloom 텍스처를 단일 샘플링하는 방식도 ALU 감소에 기여한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bloom Device Memory Write &amp;minus;54.5%.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;SRT 호출 감소의 가장 직접적인 성과다. TBDR에서 SRT마다 발생하는 타일 Store가 7회분 줄어들면서, Bloom 구간의 DRAM 쓰기 부담이 절반 이하로 감소했다. 모바일에서 DRAM 접근은 전력 소모의 주요 원인이므로, 이 감소는 서멀 마진 확보에 직접 기여한다.&lt;/p&gt;
&lt;h3 id=&quot;%EC%9A%94%EC%95%BD&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Mipmap Bloom은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;GPU Time 9.61% 감소, ALU 12.37% 감소&lt;/b&gt;를 달성했다. 대역폭 관점에서는 Bloom 파이프라인 자체의 DRAM 전송량이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;26.9% 감소&lt;/b&gt;(특히 Write &amp;minus;54.5%)되었으며, 이는 모바일 환경에서 장시간 플레이 시 발열 감소와 배터리 수명 연장에 기여한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;drawinstance-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;DrawInstance 테스트&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MipmapBlur 패스에서 4개 Mip Level을&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetViewport로 순차 렌더링하는 대신,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;GPU Instancing으로 1 draw call에 처리&lt;/b&gt;하는 방식도 테스트했다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MipmapBlur&amp;nbsp;패스에서&amp;nbsp;SetRenderTarget이&amp;nbsp;변동되지&amp;nbsp;않기&amp;nbsp;때문에&amp;nbsp;시도할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;방법이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vlNhn/dJMcagx43pB/wtmxeW6bqnHqqVmLee94fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vlNhn/dJMcagx43pB/wtmxeW6bqnHqqVmLee94fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vlNhn/dJMcagx43pB/wtmxeW6bqnHqqVmLee94fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvlNhn%2FdJMcagx43pB%2FwtmxeW6bqnHqqVmLee94fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1129&quot; height=&quot;431&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;구현 방식&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Instanced 모드에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;DrawProcedural의 인스턴스 카운트를 4로 설정하여 Mip Level마다 하나의 인스턴스를 할당한다:&lt;/p&gt;
&lt;pre class=&quot;csharp&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt; using (new ProfilingScope(cmd, ProfilingSamplers.MipmapBlurH))
{
    cmd.SetRenderTarget(data.mipmapHoriTexture, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
    cmd.ClearRenderTarget(false, true, Color.black);    
                            
    cmd.SetGlobalTexture(ShaderPropertyId.MipmapSampleTexture0, data.downSampleTextures[1]);
    cmd.SetGlobalTexture(ShaderPropertyId.MipmapSampleTexture1, data.downSampleTextures[2]);
    cmd.SetGlobalTexture(ShaderPropertyId.MipmapSampleTexture2, data.downSampleTextures[3]);
    cmd.SetGlobalTexture(ShaderPropertyId.MipmapSampleTexture3, data.downSampleTextures[4]);
                            
     cmd.DrawProcedural(Matrix4x4.identity, data.material, PassId.MipmapBlurHori_Instanced, MeshTopology.Quads, 4, ConstParams.MipmapLevel);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Vertex Shader에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;SV_InstanceID를 통해 각 인스턴스가 어떤 Mip Level에 해당하는지 판별하고, UV Tiling/Offset을 적용하여 NDC 좌표를 영역별로 배치한다:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;Varyings FullscreenVert_Instance(Attributes input, uint instanceID : SV_InstanceID)
{
    const uint mipmapLevel = instanceID;
    const float4 viewportRect = _BloomMipmapTilingOffset[mipmapLevel]; 
    output.positionCS.xy = output.positionCS.xy * viewportRect.xy + viewportRect.zw;
    output.positionCS.xy = output.positionCS.xy * float2(2.0, -2.0) + float2(-1.0, 1.0);
    // ...
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;%EC%B6%94%EA%B0%80%EB%90%9C-%EB%B9%84%EC%9A%A9&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;추가된 비용&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Instanced 모드에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetViewport&lt;span&gt;&amp;nbsp;&lt;/span&gt;대신 셰이더 내에서 영역을 결정하므로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;두 가지 추가 비용&lt;/b&gt;이 발생한다:&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 텍스처 선택 분기문.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SetViewport&lt;span&gt;&amp;nbsp;&lt;/span&gt;방식에서는 C# 코드에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;cmd.SetGlobalTexture로 해당 Mip Level의 DownSample 텍스처를 직접 바인딩한다. Instanced 방식에서는 4개 텍스처를 모두 바인딩해두고 Fragment Shader에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;SV_InstanceID에 따라 선택해야 한다:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;background-color: #f5f7fa; color: #2b2b2f; text-align: start;&quot;&gt;&lt;code&gt;half3 SampleInputTexture(uint mipmapLevel, float2 uv)
{
    [branch]
    if (mipmapLevel == 0) return SAMPLE_MIPMAP_TEXTURE(_MipmapSampleTexture0);
    else if (mipmapLevel == 1) return SAMPLE_MIPMAP_TEXTURE(_MipmapSampleTexture1);
    else if (mipmapLevel == 2) return SAMPLE_MIPMAP_TEXTURE(_MipmapSampleTexture2);
    else return SAMPLE_MIPMAP_TEXTURE(_MipmapSampleTexture3);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 분기문은 Fragment Shader의 모든 픽셀에서 실행된다. GPU에서 동적 분기는 warp/SIMD 그룹 내 divergence를 유발할 수 있고, 사용하지 않는 텍스처 슬롯까지 바인딩 상태를 유지해야 한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Viewport 대체 연산.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하드웨어&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetViewport가 무비용으로 처리하던 클리핑과 좌표 변환을 셰이더에서 직접 수행해야 한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%EC%B8%A1%EC%A0%95-%EA%B2%B0%EA%B3%BC&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;측정 결과&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Instanced 모드를 적용한 별도 캡처에서 Bloom Execution Cost가 SetViewport 방식 대비 상승하는 것을 확인했다. 텍스처 선택 분기와 좌표 변환의 추가 연산이 드로우콜 감소(4&amp;rarr;1)의 이점을 상쇄하고도 남는다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;%EA%B2%B0%EB%A1%A0%3A-%EC%95%88-%ED%95%98%EB%8A%94-%EA%B2%8C-%EB%82%AB%EB%8B%A4&quot; style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;결론: 안 하는 게 낫다&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bloom의 주요 비용 구성을 보면, 드로우콜 오버헤드보다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;블러 연산 자체의 ALU 및 텍스처 샘플링 비용&lt;/b&gt;이 지배적이다. MipmapBlur 패스의 드로우콜은&lt;span&gt;&amp;nbsp;&lt;/span&gt;SetViewport&lt;span&gt;&amp;nbsp;&lt;/span&gt;방식에서도 Mip Level당 1회씩 총 4회에 불과하다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPU Instancing으로 4 draw call &amp;rarr; 1 draw call로 줄여봐야 절감되는 드로우콜 오버헤드는 미미한 반면, 텍스처 선택 분기와 좌표 변환 연산이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;모든 픽셀에서&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;추가되어 오히려 전체 비용이 증가한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #2b2b2f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bloom과 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;draw call 수가 적고 픽셀당 연산 비용이 높은&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;풀스크린 이펙트에서는 GPU Instancing의 이점이 거의 없다. 드로우콜 최적화는 수백~수천 개의 메시를 그리는 지오메트리 패스에서 효과적이지, 패스당 풀스크린 삼각형 1장을 그리는 포스트 프로세싱에서는 적용 대상이 아니다.&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>bloom</category>
      <category>Bloom 최적화</category>
      <category>mipmap Bloom</category>
      <category>UberPost</category>
      <category>unity</category>
      <category>unity 최적화</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/195</guid>
      <comments>https://darkcatgame.tistory.com/195#entry195comment</comments>
      <pubDate>Mon, 23 Mar 2026 20:44:18 +0900</pubDate>
    </item>
    <item>
      <title>Unity URP에서 화면 효과를 최적화하는 새로운 접근법</title>
      <link>https://darkcatgame.tistory.com/194</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZCZxc111222334122224.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ryQv8/btsPt0ww129/4E7rFkEaof6BkjyscgKLo0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ryQv8/btsPt0ww129/4E7rFkEaof6BkjyscgKLo0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ryQv8/btsPt0ww129/4E7rFkEaof6BkjyscgKLo0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ryQv8/btsPt0ww129/4E7rFkEaof6BkjyscgKLo0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;353&quot; data-filename=&quot;ZCZxc111222334122224.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZCZxc11122233412224.gif&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhoN48/btsPt0J1Wwq/htVROPraksXVZhaTlBONQK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhoN48/btsPt0J1Wwq/htVROPraksXVZhaTlBONQK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhoN48/btsPt0J1Wwq/htVROPraksXVZhaTlBONQK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dhoN48/btsPt0J1Wwq/htVROPraksXVZhaTlBONQK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;346&quot; data-filename=&quot;ZCZxc11122233412224.gif&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 게임의 연출에서 이펙트 뿐만아니라 흑백 반전, 집중선 등 화면에 다양한 이펙트 표현을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 효과를 구현하는 방법은 여러가지가 있지만 Unity URP 환경에서 일반적으로 FullscreenRendererFeature &amp;amp; VolumeComponent 방식으로 제작한다면 ScriptableRenderPass, Shader를 직접 구현해야 하기 때문에 개발에 필요한 지식이 높고 관리도 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 화면 효과 구현에 대한 다양한 방법과 제가 생각하는 최적의 구현 방법을 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;화면 버퍼 복제 효과 - FrameBufferFetch 활용&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cw66m5/btsPtEln6IO/vbZFjNZB1ZiKeOXUjoKTj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cw66m5/btsPtEln6IO/vbZFjNZB1ZiKeOXUjoKTj1/img.png&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;503&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.2721%; margin-right: 10px;&quot; data-widthpercent=&quot;49.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cw66m5/btsPtEln6IO/vbZFjNZB1ZiKeOXUjoKTj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcw66m5%2FbtsPtEln6IO%2FvbZFjNZB1ZiKeOXUjoKTj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1009&quot; height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdCLta/btsPqPvThGg/ifjSK8Eua29ICVgmkHWULK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdCLta/btsPqPvThGg/ifjSK8Eua29ICVgmkHWULK/img.png&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;503&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.5651%;&quot; data-widthpercent=&quot;50.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdCLta/btsPqPvThGg/ifjSK8Eua29ICVgmkHWULK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdCLta%2FbtsPqPvThGg%2FifjSK8Eua29ICVgmkHWULK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1015&quot; height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흑백 반전 효과는 URP OpaqueTexture 혹은 직접 화면 버퍼를 복제해서 FullScreenShader에서 화면 버퍼를 샘플링하는 방식으로 구현됩니다. 많은 이펙트 연출 효과에서 사용되고 구현 원리가 단순하지만 화면 버퍼를 추가로 생성하고 샘플링해야하기 때문에 메모리 비용과 대역폭 부담이 있는 효과입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity6에서 FrameBufferFetch 기능을 활용한다면 샘플링에 의한 대역폭 사용 없이 굉장히 가볍게 구현 가능하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 메모리 비용은 플랫폼에 따라 다르지만 FrameBufferFetch를 제대로 지원한다면 복제하는 화면 버퍼는 Memoryless로 생성되어 메모리 부담도 없게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FrameBufferFetch에 대한 자세한 내용은 아래 포스팅을 참고해 주세요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://darkcatgame.tistory.com/193&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://darkcatgame.tistory.com/193&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1753188709071&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Unity6 RenderGraph 호환 RenderPass 간단 예제 &amp;amp; 주요 포인트 설명&quot; data-og-description=&quot;개요Unity6 RenderGraph 호환하기 위한 ScriptableRenderPass 개발 간단 예제입니다.RenderGraph 미지원 &amp;amp; RenderGraph Unsafe &amp;amp; RasterPass + Framebufferfetch 기반으로 간단한 흑백 화면 효과 구현합니다.Unity 6000.1.12f1 버전 &quot; data-og-host=&quot;darkcatgame.tistory.com&quot; data-og-source-url=&quot;https://darkcatgame.tistory.com/193&quot; data-og-url=&quot;https://darkcatgame.tistory.com/193&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KUPC0/hyZnutLgGH/6hMW1ybK72cUuOnZ9rHBF1/img.png?width=800&amp;amp;height=358&amp;amp;face=0_0_800_358,https://scrap.kakaocdn.net/dn/Ud4MW/hyZndy4ZNV/B8sHc0dJmnk5U9l76nOkP0/img.png?width=800&amp;amp;height=358&amp;amp;face=0_0_800_358,https://scrap.kakaocdn.net/dn/bR9u8O/hyZnwrAfNC/DEAkWbWCjiXjhlqRNvU51k/img.png?width=1125&amp;amp;height=636&amp;amp;face=0_0_1125_636&quot;&gt;&lt;a href=&quot;https://darkcatgame.tistory.com/193&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://darkcatgame.tistory.com/193&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KUPC0/hyZnutLgGH/6hMW1ybK72cUuOnZ9rHBF1/img.png?width=800&amp;amp;height=358&amp;amp;face=0_0_800_358,https://scrap.kakaocdn.net/dn/Ud4MW/hyZndy4ZNV/B8sHc0dJmnk5U9l76nOkP0/img.png?width=800&amp;amp;height=358&amp;amp;face=0_0_800_358,https://scrap.kakaocdn.net/dn/bR9u8O/hyZnwrAfNC/DEAkWbWCjiXjhlqRNvU51k/img.png?width=1125&amp;amp;height=636&amp;amp;face=0_0_1125_636');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Unity6 RenderGraph 호환 RenderPass 간단 예제 &amp;amp; 주요 포인트 설명&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요Unity6 RenderGraph 호환하기 위한 ScriptableRenderPass 개발 간단 예제입니다.RenderGraph 미지원 &amp;amp; RenderGraph Unsafe &amp;amp; RasterPass + Framebufferfetch 기반으로 간단한 흑백 화면 효과 구현합니다.Unity 6000.1.12f1 버전&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;darkcatgame.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpGpeZ/btsPr4TpE58/klQzBWkaT3J88uChBaWcq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpGpeZ/btsPr4TpE58/klQzBWkaT3J88uChBaWcq1/img.png&quot; data-alt=&quot;일반 화면 버퍼 샘플링&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpGpeZ/btsPr4TpE58/klQzBWkaT3J88uChBaWcq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpGpeZ%2FbtsPr4TpE58%2FklQzBWkaT3J88uChBaWcq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;846&quot; height=&quot;189&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일반 화면 버퍼 샘플링&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GL7xM/btsPtBvq6Fe/kn1QLh9PSBdTEpVnzyzJE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GL7xM/btsPtBvq6Fe/kn1QLh9PSBdTEpVnzyzJE0/img.png&quot; data-alt=&quot;FrameBufferFetch 활용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GL7xM/btsPtBvq6Fe/kn1QLh9PSBdTEpVnzyzJE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGL7xM%2FbtsPtBvq6Fe%2Fkn1QLh9PSBdTEpVnzyzJE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;194&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FrameBufferFetch 활용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coXBE3/btsPrNKV8d9/wI4OGHf5na9pEYx8T6n9Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coXBE3/btsPrNKV8d9/wI4OGHf5na9pEYx8T6n9Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coXBE3/btsPrNKV8d9/wI4OGHf5na9pEYx8T6n9Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoXBE3%2FbtsPrNKV8d9%2FwI4OGHf5na9pEYx8T6n9Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;530&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1 Pass Blending 활용 효과&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgHJBm/btsPstkR4Zm/SytOXHo6bqIQ7MdJLN9ukk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgHJBm/btsPstkR4Zm/SytOXHo6bqIQ7MdJLN9ukk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgHJBm/btsPstkR4Zm/SytOXHo6bqIQ7MdJLN9ukk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgHJBm%2FbtsPstkR4Zm%2FSytOXHo6bqIQ7MdJLN9ukk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1020&quot; height=&quot;509&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEJHBs/btsPrp4Cobs/BwAaVJVewDQl36rGKDEjrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEJHBs/btsPrp4Cobs/BwAaVJVewDQl36rGKDEjrk/img.png&quot; data-alt=&quot;https://github.com/MirzaBeig/Anime-Speed-Lines&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEJHBs/btsPrp4Cobs/BwAaVJVewDQl36rGKDEjrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEJHBs%2FbtsPrp4Cobs%2FBwAaVJVewDQl36rGKDEjrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;310&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/MirzaBeig/Anime-Speed-Lines&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시와 같이 화면 효과 중에 단순히 화면 위에 블랜딩하거나 덮는 효과는 화면 버퍼 복제 없이, 블랜드 옵션이 활성화된 FullScreen Shader를 활용할 수 있습니다. 반투명 오브젝트를 화면에 렌더링하면 되기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FullScreenShader에서는 구현하고자하는 효과에 알맞는 Blend 옵션을 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 ScriptableRenderPass에서 SetRenderTarget 함수에 colorLoadAction 옵션을 Load로 세팅하면, 일반적인 알파블랜드 렌더링을 실행하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XKsBY/btsPr7WSzzs/MW0J0gqyQYDRKjlE43eiE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XKsBY/btsPr7WSzzs/MW0J0gqyQYDRKjlE43eiE0/img.png&quot; data-alt=&quot;FullScreenShader&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XKsBY/btsPr7WSzzs/MW0J0gqyQYDRKjlE43eiE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXKsBY%2FbtsPr7WSzzs%2FMW0J0gqyQYDRKjlE43eiE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;468&quot; height=&quot;130&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FullScreenShader&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eO2lJQ/btsPrdpO0mX/pmZ3wJaVh8pDKCrDOG5a50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eO2lJQ/btsPrdpO0mX/pmZ3wJaVh8pDKCrDOG5a50/img.png&quot; data-alt=&quot;ScriptableRenderPass&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eO2lJQ/btsPrdpO0mX/pmZ3wJaVh8pDKCrDOG5a50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeO2lJQ%2FbtsPrdpO0mX%2FpmZ3wJaVh8pDKCrDOG5a50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;999&quot; height=&quot;548&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ScriptableRenderPass&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SetRenderTarget 함수는 위와 같이 Execute 함수 혹은 RenderGraph의 UnsafePass 구현할때는 선언 필요하지만, 아래와 같이 RasterPass로 구현한다면 따로 세팅하지 않아도 RenderGraph에서 자동으로 세팅됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBBoRw/btsPsFlibCm/mCfFnDL98Ku5OjOKndKHAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBBoRw/btsPsFlibCm/mCfFnDL98Ku5OjOKndKHAk/img.png&quot; data-alt=&quot;ScriptableRenderPass&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBBoRw/btsPsFlibCm/mCfFnDL98Ku5OjOKndKHAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBBoRw%2FbtsPsFlibCm%2FmCfFnDL98Ku5OjOKndKHAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1092&quot; height=&quot;558&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ScriptableRenderPass&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 구현 결과, 화면 버퍼 복제 과정 없이 FullScreenShader를 화면에 직접 렌더링하게 되어 1 Pass로 처리 및 추가 렌더텍스처 생성하지 않아 메모리까지 최적화됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c968em/btsPsasosdr/Gy4ZtBasbqXUvG55NQghiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c968em/btsPsasosdr/Gy4ZtBasbqXUvG55NQghiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c968em/btsPsasosdr/Gy4ZtBasbqXUvG55NQghiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc968em%2FbtsPsasosdr%2FGy4ZtBasbqXUvG55NQghiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;785&quot; height=&quot;442&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 SetRenderTarget LoadAction에 대한 내용은 아래 포스팅 참고해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://darkcatgame.tistory.com/190&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://darkcatgame.tistory.com/190&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1753106170967&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Unity RenderBufferLoadAction &amp;amp; StoreAction 옵션 설명&quot; data-og-description=&quot;개요Unity 엔진에서 ScriptableRenderPass 등, 커맨드 버퍼에 RenderTarget 설정 할때 RenderBufferLoadAction &amp;amp; StoreAction 옵션에 대한 설명입니다. 해당 옵션은 SetRenderTarget으로 설정한 렌더버퍼에 대한 데이터를 &quot; data-og-host=&quot;darkcatgame.tistory.com&quot; data-og-source-url=&quot;https://darkcatgame.tistory.com/190&quot; data-og-url=&quot;https://darkcatgame.tistory.com/190&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btCudL/hyZnqxUQVP/2CFs0ZwpIVBlfFDNHahok0/img.png?width=800&amp;amp;height=148&amp;amp;face=0_0_800_148,https://scrap.kakaocdn.net/dn/bsOVzS/hyZnuf0cTs/kBd3eWv14BQP1eKjTFqy11/img.png?width=800&amp;amp;height=148&amp;amp;face=0_0_800_148,https://scrap.kakaocdn.net/dn/dt5XbP/hyZm8db1H6/Ow48ZeDNZIRNFEjp2Xy5Ak/img.png?width=2200&amp;amp;height=1113&amp;amp;face=0_0_2200_1113&quot;&gt;&lt;a href=&quot;https://darkcatgame.tistory.com/190&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://darkcatgame.tistory.com/190&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btCudL/hyZnqxUQVP/2CFs0ZwpIVBlfFDNHahok0/img.png?width=800&amp;amp;height=148&amp;amp;face=0_0_800_148,https://scrap.kakaocdn.net/dn/bsOVzS/hyZnuf0cTs/kBd3eWv14BQP1eKjTFqy11/img.png?width=800&amp;amp;height=148&amp;amp;face=0_0_800_148,https://scrap.kakaocdn.net/dn/dt5XbP/hyZm8db1H6/Ow48ZeDNZIRNFEjp2Xy5Ak/img.png?width=2200&amp;amp;height=1113&amp;amp;face=0_0_2200_1113');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Unity RenderBufferLoadAction &amp;amp; StoreAction 옵션 설명&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요Unity 엔진에서 ScriptableRenderPass 등, 커맨드 버퍼에 RenderTarget 설정 할때 RenderBufferLoadAction &amp;amp; StoreAction 옵션에 대한 설명입니다. 해당 옵션은 SetRenderTarget으로 설정한 렌더버퍼에 대한 데이터를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;darkcatgame.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Mesh를 활용한 픽셀 비용 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 FullScreenShader 구현할때 PolarCoordinate 등, UV를 Fragment에서 재구성하는 작업을 흔하게 하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이런한 UV 재구성 작업은 높은 연산 부하를 발생하는 이슈가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제를 해결하고자, FullScreenShader 렌더링에 직접 UV를 세팅한 화면 Mesh를 활용하는 방법을 소개합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yfXfu/btsPtoXnoG6/SAQ9iakFhIMksUnP4nvC8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yfXfu/btsPtoXnoG6/SAQ9iakFhIMksUnP4nvC8K/img.png&quot; data-origin-width=&quot;1507&quot; data-origin-height=&quot;855&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.3429%; margin-right: 10px;&quot; data-widthpercent=&quot;49.92&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yfXfu/btsPtoXnoG6/SAQ9iakFhIMksUnP4nvC8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyfXfu%2FbtsPtoXnoG6%2FSAQ9iakFhIMksUnP4nvC8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1507&quot; height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjYMI6/btsPqKORNDQ/B7imvN6IORghMiWbNAmXfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjYMI6/btsPqKORNDQ/B7imvN6IORghMiWbNAmXfK/img.png&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;862&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4943%;&quot; data-widthpercent=&quot;50.08&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjYMI6/btsPqKORNDQ/B7imvN6IORghMiWbNAmXfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjYMI6%2FbtsPqKORNDQ%2FB7imvN6IORghMiWbNAmXfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1524&quot; height=&quot;862&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kxvM8/btsPrneF9Bk/TArR8Bb1DR8LTZGFMkis6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kxvM8/btsPrneF9Bk/TArR8Bb1DR8LTZGFMkis6K/img.png&quot; data-alt=&quot;UV를 원형으로 재구성하는 PolarCoordinate&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kxvM8/btsPrneF9Bk/TArR8Bb1DR8LTZGFMkis6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkxvM8%2FbtsPrneF9Bk%2FTArR8Bb1DR8LTZGFMkis6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;879&quot; height=&quot;180&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UV를 원형으로 재구성하는 PolarCoordinate&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blender 및 3dsMax와 같은 DCC 툴에서 원하는 효과에 알맞는 메쉬를 모델링합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/enkQTB/btsPsEUeRLK/TnXiInSyatWjGE8VJSmhk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/enkQTB/btsPsEUeRLK/TnXiInSyatWjGE8VJSmhk0/img.png&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;1002&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.9285%; margin-right: 10px;&quot; data-widthpercent=&quot;46.47&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/enkQTB/btsPsEUeRLK/TnXiInSyatWjGE8VJSmhk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FenkQTB%2FbtsPsEUeRLK%2FTnXiInSyatWjGE8VJSmhk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;884&quot; height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMucsq/btsPs5cI7Lk/kQjSBZdXvoukk5pWC529sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMucsq/btsPs5cI7Lk/kQjSBZdXvoukk5pWC529sk/img.png&quot; data-origin-width=&quot;1121&quot; data-origin-height=&quot;1103&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.9087%;&quot; data-widthpercent=&quot;53.53&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMucsq/btsPs5cI7Lk/kQjSBZdXvoukk5pWC529sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMucsq%2FbtsPs5cI7Lk%2FkQjSBZdXvoukk5pWC529sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1121&quot; height=&quot;1103&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;메쉬 사이즈는 유니티 기준 2m X 2m 사이즈&lt;/b&gt;&lt;/span&gt;에 맞추는 것을 권장합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Unity Cube는 1m 사이즈)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ndI9q/btsPvaEGSu2/ODn1tWttB793fqggCUoOAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ndI9q/btsPvaEGSu2/ODn1tWttB793fqggCUoOAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ndI9q/btsPvaEGSu2/ODn1tWttB793fqggCUoOAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FndI9q%2FbtsPvaEGSu2%2FODn1tWttB793fqggCUoOAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;426&quot; height=&quot;474&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Axis 축은 유니티 Quad 기준으로 FrontFace가 -Z 방향&lt;/b&gt;&lt;/span&gt;을 향하도록 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r81WP/btsPunERC6r/eqjOSwPf8wOV1tbnXp8HR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r81WP/btsPunERC6r/eqjOSwPf8wOV1tbnXp8HR1/img.png&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;441&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.2518%; margin-right: 10px;&quot; data-widthpercent=&quot;52.87&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r81WP/btsPunERC6r/eqjOSwPf8wOV1tbnXp8HR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr81WP%2FbtsPunERC6r%2FeqjOSwPf8wOV1tbnXp8HR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEl56E/btsPuFeeFgg/R4gEvPrUTxaR7qlrtiDnu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEl56E/btsPuFeeFgg/R4gEvPrUTxaR7qlrtiDnu0/img.png&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;459&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.5854%;&quot; data-widthpercent=&quot;47.13&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEl56E/btsPuFeeFgg/R4gEvPrUTxaR7qlrtiDnu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEl56E%2FbtsPuFeeFgg%2FR4gEvPrUTxaR7qlrtiDnu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ScriptableRenderPass에서는 이전까지 사용하던 DrawProcedural이 아닌, DrawMesh 함수를 활용해서 원하는 메쉬를 렌더링 할 수 있도록 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYmVTO/btsPsE7J9Jv/SfU5tsA1UOMl6rV7bfoht0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYmVTO/btsPsE7J9Jv/SfU5tsA1UOMl6rV7bfoht0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYmVTO/btsPsE7J9Jv/SfU5tsA1UOMl6rV7bfoht0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYmVTO%2FbtsPsE7J9Jv%2FSfU5tsA1UOMl6rV7bfoht0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1092&quot; height=&quot;600&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FullScreenShader 설명입니다. Vertex Stage에서는 positionOS 오브젝트 공간 버텍스 좌표를 그대로 positionCS 클립 스페이스로 보냅니다. 이때 Y 값에 _ProjectionParams.x를 곱하는데 이는 OpenGL &amp;amp; DX와 같이 좌표계가 다른 API를 대응하기 위함입니다. UNITY_UV_STARTS_AT_TOP 디파인 조건문을 통해 UV Y를 뒤집는 방법도 있지만, 평면이 아닌 입체감 있는 메쉬까지 대응하기 위해서는 _ProjectionParams를 활용하는 것을 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fragment에서는 이미 PolarCoordinate와 같이 UV가 구성된 Mesh UV 정보를 사용하기 때문에 별도의 UV 재구성이 필요 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;839&quot; data-origin-height=&quot;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvLOEm/btsPtFR8SV2/JzhnHXLY84F4fvsVwsvkmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvLOEm/btsPtFR8SV2/JzhnHXLY84F4fvsVwsvkmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvLOEm/btsPtFR8SV2/JzhnHXLY84F4fvsVwsvkmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvLOEm%2FbtsPtFR8SV2%2FJzhnHXLY84F4fvsVwsvkmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;839&quot; height=&quot;661&quot; data-origin-width=&quot;839&quot; data-origin-height=&quot;661&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 화면입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S1Oql/btsPrnMt2hS/LDizPPtl5C9aVqyRj3p2V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S1Oql/btsPrnMt2hS/LDizPPtl5C9aVqyRj3p2V0/img.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;866&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0343%; margin-right: 10px;&quot; data-widthpercent=&quot;49.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S1Oql/btsPrnMt2hS/LDizPPtl5C9aVqyRj3p2V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS1Oql%2FbtsPrnMt2hS%2FLDizPPtl5C9aVqyRj3p2V0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1346&quot; height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKAGCa/btsPrn6KaZw/ceokaxrAnNMnL9vd1AYJdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKAGCa/btsPrn6KaZw/ceokaxrAnNMnL9vd1AYJdK/img.png&quot; data-origin-width=&quot;1345&quot; data-origin-height=&quot;852&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.803%;&quot; data-widthpercent=&quot;50.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKAGCa/btsPrn6KaZw/ceokaxrAnNMnL9vd1AYJdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKAGCa%2FbtsPrn6KaZw%2FceokaxrAnNMnL9vd1AYJdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1345&quot; height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;리소스 최적화 세팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FullScreenShader에 사용하는 메쉬 및 텍스처에 대한 최적화 세팅 내용입니다. 아래 소개하는 세팅은 제작하는 화면 효과에 따라 권장 세팅이 달라질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메쉬는 일반적으로 Normal &amp;amp; Tangent 정보가 필요 없기 때문에 제거합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;968&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdZ2FI/btsPvivPMaK/3lYYKbJNL3YVKlc7W45JL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdZ2FI/btsPvivPMaK/3lYYKbJNL3YVKlc7W45JL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdZ2FI/btsPvivPMaK/3lYYKbJNL3YVKlc7W45JL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdZ2FI%2FbtsPvivPMaK%2F3lYYKbJNL3YVKlc7W45JL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;530&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;968&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nxoqp/btsPtlHWYPD/vMyuY7N9VJbLoIXcitv1z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nxoqp/btsPtlHWYPD/vMyuY7N9VJbLoIXcitv1z0/img.png&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;354&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.2912%; margin-right: 10px;&quot; data-widthpercent=&quot;50.88&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nxoqp/btsPtlHWYPD/vMyuY7N9VJbLoIXcitv1z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnxoqp%2FbtsPtlHWYPD%2FvMyuY7N9VJbLoIXcitv1z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;524&quot; height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZIQdr/btsPu8GTSHv/kr1TQdowOkWa5BiYM3DDy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZIQdr/btsPu8GTSHv/kr1TQdowOkWa5BiYM3DDy0/img.png&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;499&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.546%;&quot; data-widthpercent=&quot;49.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZIQdr/btsPu8GTSHv/kr1TQdowOkWa5BiYM3DDy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZIQdr%2FbtsPu8GTSHv%2Fkr1TQdowOkWa5BiYM3DDy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;713&quot; height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : Before 우 : After&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스처는 Mipmap을 비활성화하고, 단일 채널 무압축 포맷으로 세팅해서 용량 대비 품질을 높이도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FullScreenShader에 사용되는 텍스처는 화면에 크게 렌더링되기 때문에 타일링 텍스처를 사용해도 어느 정도 이상 품질이 필요하지만, 이와 같이 단일 채널로 사용한다면 용량 &amp;amp; 품질 둘다 이점 가질 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcvYVg/btsPvfMFVOi/v7ihI9frcrbMd8oDQ8MKs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcvYVg/btsPvfMFVOi/v7ihI9frcrbMd8oDQ8MKs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcvYVg/btsPvfMFVOi/v7ihI9frcrbMd8oDQ8MKs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcvYVg%2FbtsPvfMFVOi%2Fv7ihI9frcrbMd8oDQ8MKs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;480&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 화면입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zSpgW/btsPuLZyb4K/SXa1OxMB0VuAJdmj03OQv0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zSpgW/btsPuLZyb4K/SXa1OxMB0VuAJdmj03OQv0/img.gif&quot; style=&quot;width: 64.606%; margin-right: 10px;&quot; data-widthpercent=&quot;65.37&quot; data-filename=&quot;ZDQWEQR223.gif&quot; data-is-animation=&quot;true&quot; data-origin-height=&quot;370&quot; data-origin-width=&quot;700&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zSpgW/btsPuLZyb4K/SXa1OxMB0VuAJdmj03OQv0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzSpgW%2FbtsPuLZyb4K%2FSXa1OxMB0VuAJdmj03OQv0%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4iEVC/btsPs1CRGbH/EYy7w4rw0sQpNpiyjS9YB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4iEVC/btsPs1CRGbH/EYy7w4rw0sQpNpiyjS9YB1/img.png&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;415&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;34.63&quot; style=&quot;width: 34.2312%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4iEVC/btsPs1CRGbH/EYy7w4rw0sQpNpiyjS9YB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4iEVC%2FbtsPs1CRGbH%2FEYy7w4rw0sQpNpiyjS9YB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;416&quot; height=&quot;415&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Mesh를 활용한 픽셀 비용 최적화 - ShockWave 이펙트 구현&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음, 실제 게임 이펙트 연출에 사용될 수 있는 ShockWave 화면 이펙트 효과를 구현해봅니다.&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;화면 메쉬 제작&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표한 효과는 이전에 제작한 Polarcoordinate 형태의 메쉬를 사용해도 되지만, 더 나은 최적화를 위해 가운데를 뚫은 형태의 화면 메쉬를 제작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WBANj/btsPt3zYGYh/6q1gwUQqth4o5JU1RcqJkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WBANj/btsPt3zYGYh/6q1gwUQqth4o5JU1RcqJkk/img.png&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;958&quot; data-is-animation=&quot;false&quot; width=&quot;637&quot; height=&quot;356&quot; style=&quot;width: 64.7591%; margin-right: 10px;&quot; data-widthpercent=&quot;65.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WBANj/btsPt3zYGYh/6q1gwUQqth4o5JU1RcqJkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWBANj%2FbtsPt3zYGYh%2F6q1gwUQqth4o5JU1RcqJkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1714&quot; height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nccHm/btsPs0KH7Nk/KV4eFx6RgQq10Ptu6N3fOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nccHm/btsPs0KH7Nk/KV4eFx6RgQq10Ptu6N3fOk/img.png&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;1094&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.0781%;&quot; data-widthpercent=&quot;34.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nccHm/btsPs0KH7Nk/KV4eFx6RgQq10Ptu6N3fOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnccHm%2FbtsPs0KH7Nk%2FKV4eFx6RgQq10Ptu6N3fOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1030&quot; height=&quot;1094&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 VertexColor 알파 정보를 추가해서 별도 알파 텍스처 없이 화면 가운데에서 투명해지면서 밖으로 퍼질 수 있도록 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CJ1al/btsPus0qLAh/31qMRzgDm9FMJohYKG4tEK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CJ1al/btsPus0qLAh/31qMRzgDm9FMJohYKG4tEK/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;370&quot; data-filename=&quot;ZDQWEQR223222.gif&quot; style=&quot;width: 65.6224%; margin-right: 10px;&quot; data-widthpercent=&quot;66.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CJ1al/btsPus0qLAh/31qMRzgDm9FMJohYKG4tEK/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCJ1al%2FbtsPus0qLAh%2F31qMRzgDm9FMJohYKG4tEK%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CzZrc/btsPu9Z6L1o/mWaUitGwRjdu8qRKoFJDT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CzZrc/btsPu9Z6L1o/mWaUitGwRjdu8qRKoFJDT0/img.png&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;778&quot; data-is-animation=&quot;false&quot; width=&quot;464&quot; height=&quot;485&quot; style=&quot;width: 33.2148%;&quot; data-widthpercent=&quot;33.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CzZrc/btsPu9Z6L1o/mWaUitGwRjdu8qRKoFJDT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCzZrc%2FbtsPu9Z6L1o%2FmWaUitGwRjdu8qRKoFJDT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;745&quot; height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Dissovle Shader 제작&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레퍼런스 효과 구현에 알맞는 FullScreenShader 제작합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 Dissolve 효과를 구현했으며 FragmentShader 최적화를 위해 UV Scroll 계산은 Vertex Stage에서 처리합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1753182411217&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                half Level(half value, half add, half mul)
                {
                    return saturate(add + (value * mul));
                }

                half Linearstep(half a, half b, half x)
                {
                    return saturate((x - a) / (b - a));
                }

                half Dissolve(half alpha, half alphaProgress, half sharpness)
                {
                    half dissolveWidth = sharpness;
                    half fadeProgress = lerp(-dissolveWidth, 1.0 + dissolveWidth, alphaProgress);
                    return Linearstep(fadeProgress - dissolveWidth, fadeProgress + dissolveWidth, alpha);
                }
                
                Varyings Vert(Attributes input)
                {
                    Varyings output;
                    UNITY_SETUP_INSTANCE_ID(input);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
                    
                    float4 pos = float4(input.positionOS.x, input.positionOS.y * _ProjectionParams.x, 0.0, 1.0);
                    float2 uv = input.texcoord0;

                    float time = fmod(_Time.y, 100.0);
                    output.positionCS = pos;
                    output.color = input.color;
                    output.texcoord.xy = uv * _DissolveMap1_ST.xy + (_DissolveMap1_ST.zw * time);
                    output.texcoord.zw = uv * _DissolveMap2_ST.xy + (_DissolveMap2_ST.zw * time);
                    
                    return output;
                }
                
                half4 Frag(Varyings input) : SV_Target
                {
                    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
                    float2 uv_0 = input.texcoord.xy;
                    float2 uv_1 = input.texcoord.zw;
                    half4 vertexColor = input.color;
                    
                    half dissolve_2 = SAMPLE_TEXTURE2D_X_LOD(_DissolveMap2, sampler_DissolveMap2, uv_1, 0).r;
                    dissolve_2 = Level(dissolve_2, _DissolveMap2_LevelAdd, _DissolveMap2_LevelMul);
                    half dissolve_1 = SAMPLE_TEXTURE2D_X_LOD(_DissolveMap1, sampler_DissolveMap1, uv_0, 0).r;
                    dissolve_1 = Level(dissolve_1, _DissolveMap1_LevelAdd, _DissolveMap1_LevelMul);
                    dissolve_1 *= dissolve_2 * vertexColor.a;
                    
                    half dissolveMask = Dissolve(dissolve_1, _AlphaProgress, _Sharpness);
                    return dissolveMask * _Color;
                }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 화면입니다. 사용하는 텍스처, 파라미터 세팅에 따라서 다양한 화면 연출 효과 구현 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZDSADq1.gif&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgd9DQ/btsPu7H0OvW/j9g158xhKZ5LCUmCOEqVUK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgd9DQ/btsPu7H0OvW/j9g158xhKZ5LCUmCOEqVUK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgd9DQ/btsPu7H0OvW/j9g158xhKZ5LCUmCOEqVUK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dgd9DQ/btsPu7H0OvW/j9g158xhKZ5LCUmCOEqVUK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;379&quot; data-filename=&quot;ZDSADq1.gif&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZCZxc11122233412224.gif&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhoN48/btsPt0J1Wwq/htVROPraksXVZhaTlBONQK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhoN48/btsPt0J1Wwq/htVROPraksXVZhaTlBONQK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhoN48/btsPt0J1Wwq/htVROPraksXVZhaTlBONQK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dhoN48/btsPt0J1Wwq/htVROPraksXVZhaTlBONQK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;346&quot; data-filename=&quot;ZCZxc11122233412224.gif&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;VAT 활용한 화면 깨짐 연출 효과 구현&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 VAT(Vertex Animation Texture) 기법을 활용한 화면 깨짐 연출 효과를 구현합니다.&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;VAT 리소스 제작&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VAT(Vertex Animation Texture)는 메쉬 애니메이션의 버텍스 정보를 텍스처에 기록해서 Vertex Stage에서 샘플링하는 기법입니다. VAT 리소스 제작은 Blender, 3dsMax, 후디니 등 다양한 방법이 존재하며 저는 Blender를 활용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blender에서 Cell Fracture라는 에드온으로 파괴 시물레이션을 구현했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 : &lt;a href=&quot;https://www.youtube.com/watch?v=mEMW5elom_4&amp;amp;ab_channel=SILVERPUNK&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=mEMW5elom_4&amp;amp;ab_channel=SILVERPUNK&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1940&quot; data-origin-height=&quot;975&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8EcQG/btsPvf6YKAK/5ozM4OWo4pRSGX2uXM5Rq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8EcQG/btsPvf6YKAK/5ozM4OWo4pRSGX2uXM5Rq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8EcQG/btsPvf6YKAK/5ozM4OWo4pRSGX2uXM5Rq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8EcQG%2FbtsPvf6YKAK%2F5ozM4OWo4pRSGX2uXM5Rq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1940&quot; height=&quot;975&quot; data-origin-width=&quot;1940&quot; data-origin-height=&quot;975&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표하는 연출에 맞게 파괴 시물레이션 애니메이션 제작 후, VAT 리소스로 추출합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZDSADq14343111zz.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dNGzrQ/btsPtArjXP0/tClLkbzKcWWya44hVugqQK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dNGzrQ/btsPtArjXP0/tClLkbzKcWWya44hVugqQK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dNGzrQ/btsPtArjXP0/tClLkbzKcWWya44hVugqQK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dNGzrQ/btsPtArjXP0/tClLkbzKcWWya44hVugqQK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;476&quot; data-filename=&quot;ZDSADq14343111zz.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제작하는 효과는 유리 표현을 위해 Fresnel 표현이 필요하기 때문에 Normal정보까지 필요합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;230&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MMBOR/btsPtDuUGQJ/KtNZEMckaWmTwJzyvG8gK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MMBOR/btsPtDuUGQJ/KtNZEMckaWmTwJzyvG8gK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MMBOR/btsPtDuUGQJ/KtNZEMckaWmTwJzyvG8gK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMMBOR%2FbtsPtDuUGQJ%2FKtNZEMckaWmTwJzyvG8gK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;186&quot; data-origin-width=&quot;230&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;VAT Shader 제작&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VAT Shader는 이전 Mesh FullScreenShader와 동일하지만, PositionOS에 VAT Position&amp;nbsp; 샘플 데이터를 Offset 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VAT Normal 샘플 데이터는 NormalOS로 그대로 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGpuje/btsPukVMTva/EXzrqcKipRZiV5bNdXJoY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGpuje/btsPukVMTva/EXzrqcKipRZiV5bNdXJoY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGpuje/btsPukVMTva/EXzrqcKipRZiV5bNdXJoY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGpuje%2FbtsPukVMTva%2FEXzrqcKipRZiV5bNdXJoY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1079&quot; height=&quot;464&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 테스트 삼아, CopyColor 텍스처를 샘플링해서 효과 구현해보면 아래와 같이 화면이 깨지는 효과 구현이 완료됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZDSADq14343111.gif&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z6pIB/btsPvhRgtUt/W2ipl4OUuLBNBzhRJHcqo1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z6pIB/btsPvhRgtUt/W2ipl4OUuLBNBzhRJHcqo1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z6pIB/btsPvhRgtUt/W2ipl4OUuLBNBzhRJHcqo1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Z6pIB/btsPvhRgtUt/W2ipl4OUuLBNBzhRJHcqo1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;900&quot; height=&quot;279&quot; data-filename=&quot;ZDSADq14343111.gif&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;279&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;유리 깨지는 표현 강화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shader에서 추가로 Fresnel 효과와 화면 버퍼 샘플링 UV에 Normal 방향으로 Offset 처리해서 유리의 굴절과 반사 효과를 구현해서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;유리 재질 표현을 강화합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H13wG/btsPtCJrw5m/uwd4TUPmAR2j6XHeKDnkK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H13wG/btsPtCJrw5m/uwd4TUPmAR2j6XHeKDnkK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H13wG/btsPtCJrw5m/uwd4TUPmAR2j6XHeKDnkK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH13wG%2FbtsPtCJrw5m%2Fuwd4TUPmAR2j6XHeKDnkK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1098&quot; height=&quot;738&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvdAP0/btsPt64zPbk/MrXroaAUmlxxjF6r8KYCx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvdAP0/btsPt64zPbk/MrXroaAUmlxxjF6r8KYCx0/img.png&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;431&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.6751%; margin-right: 10px;&quot; data-widthpercent=&quot;50.26&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvdAP0/btsPt64zPbk/MrXroaAUmlxxjF6r8KYCx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvdAP0%2FbtsPt64zPbk%2FMrXroaAUmlxxjF6r8KYCx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;867&quot; height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/quY71/btsPuMxtqLl/DffDkN4rhEr5JAKoekq5y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/quY71/btsPuMxtqLl/DffDkN4rhEr5JAKoekq5y1/img.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;436&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.1621%;&quot; data-widthpercent=&quot;49.74&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/quY71/btsPuMxtqLl/DffDkN4rhEr5JAKoekq5y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FquY71%2FbtsPuMxtqLl%2FDffDkN4rhEr5JAKoekq5y1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;868&quot; height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : Before 우 : After&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 임시로 CopyColor를 샘플링하도록 했지만, Opaque 패스 이후에 복제된 버퍼이기 때문에 Transparnet 패스까지 대응하기 위해 새로 화면 버퍼를 복제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시에서는 renderGraph.AddCopyPass 함수를 활용했으며, 해당 함수를 사용하면 FrameBufferFetch로 화면 버퍼를 복제할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kBBDg/btsPualuE3o/v3c4CNoHEKuGGbZJZ8LA6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kBBDg/btsPualuE3o/v3c4CNoHEKuGGbZJZ8LA6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kBBDg/btsPualuE3o/v3c4CNoHEKuGGbZJZ8LA6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkBBDg%2FbtsPualuE3o%2Fv3c4CNoHEKuGGbZJZ8LA6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1070&quot; height=&quot;802&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;애니메이션 제작 &amp;amp; 결과 화면&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VAT Shader의 파라미터를 제어해서 Animation을 하나 제작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buTrrJ/btsPt6XOTAe/a1VRmJJIOFz3ENuCje2Fck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buTrrJ/btsPt6XOTAe/a1VRmJJIOFz3ENuCje2Fck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buTrrJ/btsPt6XOTAe/a1VRmJJIOFz3ENuCje2Fck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuTrrJ%2FbtsPt6XOTAe%2Fa1VRmJJIOFz3ENuCje2Fck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1036&quot; height=&quot;249&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZCZxc111222334122224.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ryQv8/btsPt0ww129/4E7rFkEaof6BkjyscgKLo0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ryQv8/btsPt0ww129/4E7rFkEaof6BkjyscgKLo0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ryQv8/btsPt0ww129/4E7rFkEaof6BkjyscgKLo0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ryQv8/btsPt0ww129/4E7rFkEaof6BkjyscgKLo0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;353&quot; data-filename=&quot;ZCZxc111222334122224.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;화면 연출 효과를 위한 Renderer 컴포넌트 제작&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 소개한 화면 연출 효과는 항상 렌더링되는 것이 아닌, 일반 Particle 이펙트와 같이 일시적으로 생성되고 제거되는 용도입니다. 쉽게 말해서 Bloom, SSAO와 같은 Postprocess와 용도 자체가 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 일반적으로 ScriptableRendererFeature에서 렌더패스를 호출하는 방식으로 제작한다면 항상 Universal Renderer에 객체가 유지되고 Volume Componenet 등을 통해서 활성화 여부와 파라미터를 제어할 수 밖에 없는데 이러면 따로 제어하는 시스템을 구현 할 수밖에 없는 비효율이 발생합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BzQl7/btsPvhwYDRt/3FfN18OJYI3wJEwr0eElu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BzQl7/btsPvhwYDRt/3FfN18OJYI3wJEwr0eElu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BzQl7/btsPvhwYDRt/3FfN18OJYI3wJEwr0eElu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBzQl7%2FbtsPvhwYDRt%2F3FfN18OJYI3wJEwr0eElu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;461&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 소개할 내용은 ScriptableRendererFeature를 사용하지 않고 ScriptableRenderPass를 직접 호출하는 Monobehaviour 객체를 구현해서 오브젝트로 효과를 관리할 수 있는 방법을 소개합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvQa8o/btsPt2nyRKw/Iz4mN1Yv50Q5nrq4GVj870/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvQa8o/btsPt2nyRKw/Iz4mN1Yv50Q5nrq4GVj870/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvQa8o/btsPt2nyRKw/Iz4mN1Yv50Q5nrq4GVj870/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvQa8o%2FbtsPt2nyRKw%2FIz4mN1Yv50Q5nrq4GVj870%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;449&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;RenderPipelineManager.beginCameraRendering&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderPipelineManager.beginCameraRendering 이벤트 함수 등을 활용해서 URP 내부 렌더링 로직 호출 시점에 원하는 로직이 호출 가능합니다. 이를 활용해 scriptableRenderer에 원하는 ScriptableRenderPass 객체를 렌더링 Queue에 등록하는 방식으로 ScriptableRendererFeature 없이 렌더 패스 호출하도록 구현합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9xWis/btsPtYligYM/TtTxkBjN6hVVUOAftywRaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9xWis/btsPtYligYM/TtTxkBjN6hVVUOAftywRaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9xWis/btsPtYligYM/TtTxkBjN6hVVUOAftywRaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9xWis%2FbtsPtYligYM%2FTtTxkBjN6hVVUOAftywRaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;265&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU4YIi/btsPtWntXhw/pH3KfKRqb7pFtQR0hQxxfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU4YIi/btsPtWntXhw/pH3KfKRqb7pFtQR0hQxxfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU4YIi/btsPtWntXhw/pH3KfKRqb7pFtQR0hQxxfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU4YIi%2FbtsPtWntXhw%2FpH3KfKRqb7pFtQR0hQxxfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;759&quot; height=&quot;600&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 실제 유리 파괴 효과를 렌더링하도록 구현한 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1753186037746&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; [ExecuteInEditMode]
    public class GlassBreakFX : MonoBehaviour
    {
        [SerializeField] private RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        [SerializeField] private Camera targetCamera;
        [SerializeField] private Material material;
        [SerializeField] private Mesh mesh;
        [SerializeField] private GlassBreakFXRenderPass.MaterialProperties materialProperties = GlassBreakFXRenderPass.MaterialProperties.Default();
        
        private GlassBreakFXRenderPass _renderPass;
        
        private void OnEnable()
        {
            if(!targetCamera) targetCamera = Camera.main;
            _renderPass = new GlassBreakFXRenderPass();
            RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;
        }

        private void OnDisable()
        {
            _renderPass?.Dispose();
            _renderPass = null;
            RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;
        }

        private void OnBeginCameraRendering(ScriptableRenderContext context, Camera cam)
        {
            if (material == null || mesh==null ||
                targetCamera == null || targetCamera != cam || 
                !targetCamera.isActiveAndEnabled) return;
            
            var cameraData = cam.GetUniversalAdditionalCameraData();
            if (cameraData.renderType != CameraRenderType.Base) return;
            
            _renderPass.renderPassEvent = renderPassEvent;
            _renderPass.material = material;
            _renderPass.mesh = mesh;
            _renderPass.materialProperties = materialProperties;
            cameraData.scriptableRenderer.EnqueuePass(_renderPass);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;리소스 관리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Monobehaviour 클래스에서 FullScreenShader의 파라미터를 노출 시키면 이펙트 작업자는 Particle System 등으로 이펙트 제작과 함께 화면 연출 효과 객체를 다룰 수 있으며, Particle System과 함께 Prefab으로 묶어서 관리 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd7gtt/btsPs5yFQ6C/GwGzBwFNZ3y5lOda2f5amK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd7gtt/btsPs5yFQ6C/GwGzBwFNZ3y5lOda2f5amK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd7gtt/btsPs5yFQ6C/GwGzBwFNZ3y5lOda2f5amK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd7gtt%2FbtsPs5yFQ6C%2FGwGzBwFNZ3y5lOda2f5amK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;459&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWYiYZ/btsPuD1QNJI/6jposStA9pLxBUlxiZoFG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWYiYZ/btsPuD1QNJI/6jposStA9pLxBUlxiZoFG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWYiYZ/btsPuD1QNJI/6jposStA9pLxBUlxiZoFG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWYiYZ%2FbtsPuD1QNJI%2F6jposStA9pLxBUlxiZoFG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;923&quot; height=&quot;388&quot; data-origin-width=&quot;923&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKsTRR/btsPs5ZKIcD/GIogkLLnYyXT3MkKxStbo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKsTRR/btsPs5ZKIcD/GIogkLLnYyXT3MkKxStbo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKsTRR/btsPs5ZKIcD/GIogkLLnYyXT3MkKxStbo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKsTRR%2FbtsPs5ZKIcD%2FGIogkLLnYyXT3MkKxStbo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;574&quot; height=&quot;219&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>framebufferfetch</category>
      <category>GlassBreak</category>
      <category>PostProcess Effect</category>
      <category>PostProcessFX</category>
      <category>rendergraph</category>
      <category>Screen VAT</category>
      <category>ScreenEffects</category>
      <category>ScreenFX</category>
      <category>unity</category>
      <category>unity6</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/194</guid>
      <comments>https://darkcatgame.tistory.com/194#entry194comment</comments>
      <pubDate>Tue, 22 Jul 2025 21:50:57 +0900</pubDate>
    </item>
    <item>
      <title>Unity6 RenderGraph 호환 RenderPass 간단 예제 &amp;amp; 주요 포인트 설명</title>
      <link>https://darkcatgame.tistory.com/193</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baHJ56/btsPmxUv7wm/LrPSyKRfINc62KqDJM3Lwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baHJ56/btsPmxUv7wm/LrPSyKRfINc62KqDJM3Lwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baHJ56/btsPmxUv7wm/LrPSyKRfINc62KqDJM3Lwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaHJ56%2FbtsPmxUv7wm%2FLrPSyKRfINc62KqDJM3Lwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1090&quot; height=&quot;604&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6J6Hn/btsPlOCELnA/clkAwHL5qy84bfvhEArskk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6J6Hn/btsPlOCELnA/clkAwHL5qy84bfvhEArskk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6J6Hn/btsPlOCELnA/clkAwHL5qy84bfvhEArskk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6J6Hn%2FbtsPlOCELnA%2FclkAwHL5qy84bfvhEArskk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1073&quot; height=&quot;481&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity6 RenderGraph 호환하기 위한 ScriptableRenderPass 개발 간단 예제입니다.&lt;/li&gt;
&lt;li&gt;RenderGraph 미지원 &amp;amp; RenderGraph Unsafe &amp;amp; RasterPass + Framebufferfetch 기반으로 간단한 흑백 화면 효과 구현합니다.&lt;/li&gt;
&lt;li&gt;Unity 6000.1.12f1 버전 기준으로 개발되었습니다.&lt;/li&gt;
&lt;li&gt;예제 코드에 대한 상세한 설명 대신, RenderGraph 호환 개발에 대한 핵심 포인트만 설명합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;샘플 코드&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/UnityRenderGraphSample&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/CatDarkGame/UnityRenderGraphSample&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1752670956162&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - CatDarkGame/UnityRenderGraphSample: Unity 6000.1.12f1&quot; data-og-description=&quot;Unity 6000.1.12f1 . Contribute to CatDarkGame/UnityRenderGraphSample development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/CatDarkGame/UnityRenderGraphSample&quot; data-og-url=&quot;https://github.com/CatDarkGame/UnityRenderGraphSample&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c75MkL/hyZjgCYfLb/StUh8nZLt2L2WZo1Kanwzk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/8BndV/hyZnn1vj3T/GS8y68uaq9al5YcxxRB9D1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/UnityRenderGraphSample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/CatDarkGame/UnityRenderGraphSample&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c75MkL/hyZjgCYfLb/StUh8nZLt2L2WZo1Kanwzk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/8BndV/hyZnn1vj3T/GS8y68uaq9al5YcxxRB9D1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - CatDarkGame/UnityRenderGraphSample: Unity 6000.1.12f1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Unity 6000.1.12f1 . Contribute to CatDarkGame/UnityRenderGraphSample development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RenderGraph 호환 개발 핵심 포인트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. RenderGraph 활성화 상태에서 Execute 대신, RecordRenderGraph 함수 사용.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 ScriptableRenderPass의 Execute 함수는 ScriptableRenderer.EnqueuePass에 의해서 호출되며 커맨드버퍼 명령 로직을 작성합니다. RenderGraph 활성화 상태에서는 Execute 대신 RecordRenderGraph 함수가 호출됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderGraph 활성화 &amp;amp; 비활성화 환경 둘다 대응하고 싶으면 두 함수 모두 구현해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Execute 함수와 같이 RenderGraph 이전에 사용하던 함수에는 [Obsolete] 어트리뷰트를 추가합니다, 왜냐하면 deprecated 예정이라고 경고 메세지가 계속 발생하기 때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vz87J/btsPlty0llt/ZdtIlkFfilVsF2p127RqP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vz87J/btsPlty0llt/ZdtIlkFfilVsF2p127RqP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vz87J/btsPlty0llt/ZdtIlkFfilVsF2p127RqP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVz87J%2FbtsPlty0llt%2FZdtIlkFfilVsF2p127RqP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;797&quot; height=&quot;130&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5YFX9/btsPlixwCSH/RFm7fMX0YQvEHqAkYdOtPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5YFX9/btsPlixwCSH/RFm7fMX0YQvEHqAkYdOtPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5YFX9/btsPlixwCSH/RFm7fMX0YQvEHqAkYdOtPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5YFX9%2FbtsPlixwCSH%2FRFm7fMX0YQvEHqAkYdOtPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;780&quot; height=&quot;131&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. RenderTexture는 RenderGraph 시스템에서 관리, RTHandle 사용하지 않음.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderGraph의 방향성은 RenderTexture 객체 데이터를 사용자가 관리하지 않고 RenderGraph 시스템에서 관리하는 것이며 사용자가 RTHandle과 같은 방법으로 RenderTexture 객체 메모리 관리하지 않습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTHandle 대신, TextureHandle 구조체를 사용해서 RenderGraph에서 관리하는 RenderTexture를 참조합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 TextureHandle은 사용자 클래스에 직접 선언해서 관리하지 않고 PassData 컨테이너 클래스를 선언해서 내부에 선언합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cm3iPD/btsPk2BIB9T/ibvQN6VMIvPvs0WcQxs7WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cm3iPD/btsPk2BIB9T/ibvQN6VMIvPvs0WcQxs7WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cm3iPD/btsPk2BIB9T/ibvQN6VMIvPvs0WcQxs7WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcm3iPD%2FbtsPk2BIB9T%2FibvQN6VMIvPvs0WcQxs7WK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;202&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. PassData 컨테이너 클래스&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 설명한 PassData는 RenderGraph 시스템에 전달 &amp;amp; 참조하기 위한 컨테이너 클래스입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TextureHandle &amp;amp; Material 등, 렌더링에 필요한 자료형을 선언합니다. PassData 또한 ScriptableRenderPass에 객체를 선언하지 않고 RecordRenderGraph 함수 내부에서 RenderGraph에 전달 &amp;amp; 참조만 합니다. RenderGraph에서는 PassData 클래스 내용물을 기준으로 메모리를 할당하여 실제 데이터는 RenderGraph 내부에서 관리하는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B43Oj/btsPlQtNJOL/oRfBluVR0UyFALKzx8HHfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B43Oj/btsPlQtNJOL/oRfBluVR0UyFALKzx8HHfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B43Oj/btsPlQtNJOL/oRfBluVR0UyFALKzx8HHfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB43Oj%2FbtsPlQtNJOL%2FoRfBluVR0UyFALKzx8HHfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;422&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. UnsafePass와 RasterPass&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderGraph의 SubPass는 UnsafePass와 RasterPass 2가지 방법으로 개발합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UnsafePass는 기존 처럼 CommandBuffer API를 대부분 다 사용 가능합니다, 실제로 RenderGraph 호환 코드로 변경할때 가장 쉬운 선택지입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xBxP2/btsPmUopIaW/ZExtIEUeskODuUdcdHZ8N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xBxP2/btsPmUopIaW/ZExtIEUeskODuUdcdHZ8N1/img.png&quot; data-alt=&quot;UnsafePass&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xBxP2/btsPmUopIaW/ZExtIEUeskODuUdcdHZ8N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxBxP2%2FbtsPmUopIaW%2FZExtIEUeskODuUdcdHZ8N1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;636&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UnsafePass&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RasterPass는 NativeRenderPass로 동작하는 SubPass입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CommandBuffer 클래스가 아닌 RasterCommandBuffer 클래스를 활용해야하며, 기존 CommandBuffer API 사용이 안됩니다. 대표적으로 SetRenderTarget 대신, builder.SetRenderAttachment 함수를 이용해야합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;927&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z1zvM/btsPk3ggtcW/qkQ0jpXCfU1GMJCEoj3E4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z1zvM/btsPk3ggtcW/qkQ0jpXCfU1GMJCEoj3E4k/img.png&quot; data-alt=&quot;RasterPass&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z1zvM/btsPk3ggtcW/qkQ0jpXCfU1GMJCEoj3E4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ1zvM%2FbtsPk3ggtcW%2FqkQ0jpXCfU1GMJCEoj3E4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1159&quot; height=&quot;927&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;927&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RasterPass&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UnsafePass &amp;amp; RasterPass 차이점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RasterPass는 1 SubPass당 1 Drawcall만 가능합니다, 위 예제 처럼 2번 Drawcall해야한다면 SubPass 2개 선언해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NativeRenderPass의 Pass 병합 혹은 FrameBufferFetch 사용할게 아니라면 그냥 UnsafePass로 개발해도 성능 차이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. builder.SetRenderFunc static 람다 함수 호출 권장&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;builder.SetRenderFunc 함수를 통해 람다 함수를 호출하는 방식으로 구현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 호출하는 람다 함수는 static으로 선언해야 합니다. 왜냐하면 람다 함수가 자신의 외부 스코프에 있는 변수를 &quot;캡처&quot;하면, 컴파일러는 이 변수들을 저장하기 위한 보이지 않는 클래스를 만들고 이 클래스의 인스턴스를 힙(Heap) 메모리에 할당하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해서 GC 발생 할 수 있다는 것 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MaK88/btsPmz5UfIy/cl0kv1xs1Po5TuYFBIJ0RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MaK88/btsPmz5UfIy/cl0kv1xs1Po5TuYFBIJ0RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MaK88/btsPmz5UfIy/cl0kv1xs1Po5TuYFBIJ0RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMaK88%2FbtsPmz5UfIy%2Fcl0kv1xs1Po5TuYFBIJ0RK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1065&quot; height=&quot;157&quot; data-origin-width=&quot;1065&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6. FrameBufferFetch 렌더 호출 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FrameBufferFetch를 통해 샘플링하기 위해서 아래 조건이 충족되어야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU &amp;amp; Graphics API가 타일 렌더 기반으로 동작해야함. (Vulkan 지원 급 모바일은 대부분 OK, 그 외 DX 계열은 안됨)&lt;/li&gt;
&lt;li&gt;이전 렌더링 화면 버퍼와 Target 버퍼와 해상도, 포맷 등 RenderTexture 속성이 일치해야함.&lt;/li&gt;
&lt;li&gt;UV offset 불가능, 렌더링하는 픽셀 위치의 픽셀만 샘플링 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FrameBufferFetch Pass를 렌더링할때 Texture2D가 아닌 SetInputAttachment 함수를 통해 렌더타겟과 인덱스를 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Shader에서는 렌더텍스처 이름이 아닌, 인덱스로 바인드합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAlByU/btsPlSZim5A/09cS4Doo1rDjkqb0Vk1DRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAlByU/btsPlSZim5A/09cS4Doo1rDjkqb0Vk1DRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAlByU/btsPlSZim5A/09cS4Doo1rDjkqb0Vk1DRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAlByU%2FbtsPlSZim5A%2F09cS4Doo1rDjkqb0Vk1DRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1161&quot; height=&quot;436&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NVz0N/btsPmwuyxDw/fxihRKkeW9sKHLRZwzkKuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NVz0N/btsPmwuyxDw/fxihRKkeW9sKHLRZwzkKuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NVz0N/btsPmwuyxDw/fxihRKkeW9sKHLRZwzkKuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNVz0N%2FbtsPmwuyxDw%2FfxihRKkeW9sKHLRZwzkKuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;223&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;FrameBufferFetch는 좋은가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FrameBufferFetch는 메모리에 저장된 텍스처가 아닌 타일 메모리에 저장된 픽셀 정보를 바인드하기 때문에 대역폭 사용량이 없고 샘플링 속도가 빠릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 아직 Unity6에서는 사용하기에는 불안정합니다, 가끔 렌더버퍼 바인드가 오류나는지 이유 없이 무한 튕김 증상이 일어날 때도 있고, Memoryless로 동작해야하는데 아직 iOS Metal API 외에 Vulkan 사용하는 안드로이드 계열에서 메모리에 RenderTexture를 저장하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Skp3E/btsPkKH3AUY/EpgqmW4uAymZjBN36mzWkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Skp3E/btsPkKH3AUY/EpgqmW4uAymZjBN36mzWkk/img.png&quot; data-alt=&quot;https://docs.unity3d.com/6000.1/Documentation/ScriptReference/RenderTexture-memorylessMode.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Skp3E/btsPkKH3AUY/EpgqmW4uAymZjBN36mzWkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSkp3E%2FbtsPkKH3AUY%2FEpgqmW4uAymZjBN36mzWkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1452&quot; height=&quot;446&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.unity3d.com/6000.1/Documentation/ScriptReference/RenderTexture-memorylessMode.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>framebufferfetch</category>
      <category>rasterpass</category>
      <category>rendergraph</category>
      <category>unity6</category>
      <category>unsafepass</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/193</guid>
      <comments>https://darkcatgame.tistory.com/193#entry193comment</comments>
      <pubDate>Wed, 16 Jul 2025 22:45:23 +0900</pubDate>
    </item>
    <item>
      <title>Unity PerObjectRenderTexture UI 시스템 개발 과정</title>
      <link>https://darkcatgame.tistory.com/192</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;PerObjectRT 시스템 소개&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PerObjectRT 시스템은 3D 월드 렌더러 객체를 별도 RenderTexture에 Atlas 형식으로 렌더링하고 해당 RenderTexture를 UGUI에서 참조해서 렌더링하는 시스템입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 목적은 Spine 오브젝트 다수를 UGUI에 한번에 렌더링하기 위해 개발했으며, 그 외에도 파티클 시스템이나, 일반 Mesh Renderer &amp;amp; Skinned Mesh Renderer까지 동일하게 사용 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZCZxc111222334.gif&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOdBa6/btsPkI21vUw/KNr0cy39ZbNMxMupKMQwOK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOdBa6/btsPkI21vUw/KNr0cy39ZbNMxMupKMQwOK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOdBa6/btsPkI21vUw/KNr0cy39ZbNMxMupKMQwOK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cOdBa6/btsPkI21vUw/KNr0cy39ZbNMxMupKMQwOK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;291&quot; data-filename=&quot;ZCZxc111222334.gif&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;291&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spine에서 제공하는 Skeleton Render Texture와 목적은 비슷하지만, PerObjectRT 시스템은 3D 월드에서 Spine에 파티클 시스템 및 기타 Renderer 오브젝트를 합친 결과물을 드로우콜로 한번으로 UGUI에 출력할 수 있는 차이점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 URP 렌더파이프라인이 아닌, 직접 CommandBuffer에 렌더링하는 시스템으로 개발되어, 카메라 없이 단독으로 렌더링 가능합니다. 따라서 불필요한 Culling &amp;amp; Sorting 등 비용이 발생하지 않고 오직 원하는 오브젝트를 RenderTexture에 렌더링하는 비용만 발생합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;997&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC00Ct/btsPjo5xjec/xzwgQtH1HyDVOrtxzmuHeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC00Ct/btsPjo5xjec/xzwgQtH1HyDVOrtxzmuHeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC00Ct/btsPjo5xjec/xzwgQtH1HyDVOrtxzmuHeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC00Ct%2FbtsPjo5xjec%2FxzwgQtH1HyDVOrtxzmuHeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;687&quot; height=&quot;345&quot; data-origin-width=&quot;997&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;커스텀 랜더파이프라인&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 렌더파이프라인은 모두 카메라에서 수집한 정보를 기반으로 동작합니다, 그래서 카메라에서 수행하는 Culling &amp;amp; Sorting 등 비용이 발생하게 되는데, 제작하는 시스템에서는 원하는 오브젝트만 RenderTexture에 렌더링하는 것이기 때문에 이러한 작업은 불필요한 비용 발생입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 URP 렌더파이프라인을 이용하지 않고 직접 커맨드버퍼에 ViewProjection 정보를 세팅하고 메쉬를 드로우하는 시스템 개발을 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1343&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwrAMR/btsPj8HZm27/pCiUujr4Akjk8lNUtym2aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwrAMR/btsPj8HZm27/pCiUujr4Akjk8lNUtym2aK/img.png&quot; data-alt=&quot;1. PerObjectRT시스템 메인쓰레드 비용, 2. 기본 카메라 메인쓰레드 비용.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwrAMR/btsPj8HZm27/pCiUujr4Akjk8lNUtym2aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwrAMR%2FbtsPj8HZm27%2FpCiUujr4Akjk8lNUtym2aK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1343&quot; height=&quot;350&quot; data-origin-width=&quot;1343&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1. PerObjectRT시스템 메인쓰레드 비용, 2. 기본 카메라 메인쓰레드 비용.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 렌더파이프라인을 이용하는 경우, UniversalCameraData로부터 Culling &amp;amp; Sorting 등 처리가 완료된 카메라 프러스텀에 보이는 RendererList가 들어오게 되며, 해당 데이터를 기반으로 렌더링 호출하도록 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 RendererList나, CullingResult 데이터는 URP 패키지 Internel로 되어 있거나 Unity 블랙박스 코드로 되어 있어 접근 및 수정이 불가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ9t3F/btsPiZrEqK6/eTKjq2gldo4rvmgP7dbli1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ9t3F/btsPiZrEqK6/eTKjq2gldo4rvmgP7dbli1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ9t3F/btsPiZrEqK6/eTKjq2gldo4rvmgP7dbli1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ9t3F%2FbtsPiZrEqK6%2FeTKjq2gldo4rvmgP7dbli1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1051&quot; height=&quot;395&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XuTX2/btsPkpCDYYV/phJkLfvmFwJ5w4c5su5HL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XuTX2/btsPkpCDYYV/phJkLfvmFwJ5w4c5su5HL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XuTX2/btsPkpCDYYV/phJkLfvmFwJ5w4c5su5HL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXuTX2%2FbtsPkpCDYYV%2FphJkLfvmFwJ5w4c5su5HL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1009&quot; height=&quot;105&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;105&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 커맨드 버퍼로 렌더링 호출하고 싶은 경우, 우선 RenderPipelineManager 렌더링 콜백 함수를 통해 렌더링 호출 이벤트를 받을 수 있으며, 해당 함수 내부에 커맨드 버퍼 명령을 구현합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/60C6h/btsPkvW831X/N4Ggol4YCQBXWO33C0lWWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/60C6h/btsPkvW831X/N4Ggol4YCQBXWO33C0lWWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/60C6h/btsPkvW831X/N4Ggol4YCQBXWO33C0lWWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F60C6h%2FbtsPkvW831X%2FN4Ggol4YCQBXWO33C0lWWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;333&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PerObjectRT 시스템은 사전에 수집한 Renderer 정보를 직접 생성한 RenderTexture에 렌더링하도록 구현했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 카메라가 없기 때문에 View &amp;amp; Projection Matrix를 각 오브젝트가 순차적으로 캡처되도록 해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행렬 정보 계산은 다음 파트에서 설명합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;573&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1Am3h/btsPi1XmCzF/RGkq7MREEaD40hUXk6EDZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1Am3h/btsPi1XmCzF/RGkq7MREEaD40hUXk6EDZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1Am3h/btsPi1XmCzF/RGkq7MREEaD40hUXk6EDZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1Am3h%2FbtsPi1XmCzF%2FRGkq7MREEaD40hUXk6EDZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1037&quot; height=&quot;573&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;573&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;909&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sw00D/btsPkuKKecM/ur5H1u8sqYAmBWT4oLKArK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sw00D/btsPkuKKecM/ur5H1u8sqYAmBWT4oLKArK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sw00D/btsPkuKKecM/ur5H1u8sqYAmBWT4oLKArK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsw00D%2FbtsPkuKKecM%2Fur5H1u8sqYAmBWT4oLKArK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;909&quot; height=&quot;505&quot; data-origin-width=&quot;909&quot; data-origin-height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; PerObject Renderer 객체 정보 수집, Bounds &amp;amp; VP Matrix 계산&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Monobehaviour 객체로 Renderer 및 Bounds, Transform을 수집합니다. 해당 Monobehaviour 컴포넌트가 붙은 오브젝트는 PerObjectRT 렌더 시스템에 렌더링하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TKE53/btsPle1toDv/lC1DKmhvzowLQn74qCcMkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TKE53/btsPle1toDv/lC1DKmhvzowLQn74qCcMkK/img.png&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;448&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;39.64&quot; style=&quot;width: 39.1785%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TKE53/btsPle1toDv/lC1DKmhvzowLQn74qCcMkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTKE53%2FbtsPle1toDv%2FlC1DKmhvzowLQn74qCcMkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lXAk2/btsPkslR5qG/1NwbYfHhqN4LkWbKmHLYTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lXAk2/btsPkslR5qG/1NwbYfHhqN4LkWbKmHLYTk/img.png&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;324&quot; data-is-animation=&quot;false&quot; style=&quot;width: 59.6587%;&quot; data-widthpercent=&quot;60.36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lXAk2/btsPkslR5qG/1NwbYfHhqN4LkWbKmHLYTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlXAk2%2FbtsPkslR5qG%2F1NwbYfHhqN4LkWbKmHLYTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집한 Bounds 및 Transform 정보는 단일 매니저 클래스에 Register(등록)해서 단일 List로 관리됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록되는 시점에 LocalBounds와 View &amp;amp; Projection 행렬 정보를 계산합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZWcpG/btsPjIJweTC/aAhdAhBjHlJFRC3xWFGLq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZWcpG/btsPjIJweTC/aAhdAhBjHlJFRC3xWFGLq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZWcpG/btsPjIJweTC/aAhdAhBjHlJFRC3xWFGLq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZWcpG%2FbtsPjIJweTC%2FaAhdAhBjHlJFRC3xWFGLq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;546&quot; height=&quot;268&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OtxNi/btsPi3U7nYe/NzNdIHu1MLMjsa7ZCzzto0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OtxNi/btsPi3U7nYe/NzNdIHu1MLMjsa7ZCzzto0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OtxNi/btsPi3U7nYe/NzNdIHu1MLMjsa7ZCzzto0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOtxNi%2FbtsPi3U7nYe%2FNzNdIHu1MLMjsa7ZCzzto0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;643&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 List로 관리하는 이유는, 다수의 PerObjectRT Renderer 객체들의 OrthoViewProjection 행렬 값을 한번에 병렬 계산하기 위함입니다. 병렬 계산은 Job &amp;amp; BurstCompile을 활용했으며 이를 위해 모든 데이터는 Value 타입을 사용하여 Native 자료형을 사용할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 Job을 활용할때 Native 임시 객체를 생성하여 값을 복사하는 과정을 거치는데, 저는 unsafe 방식으로 구현해서, 직접 메모리 포인터(주소)값을 참조하도록 하여, 객체 복사 없이 Job을 호출하도록 했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1752582056662&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[BurstCompile]
        private unsafe struct CalculateViewProjJobParallel : IJobParallelFor
        {
            [NativeDisableUnsafePtrRestriction][ReadOnly] public float3* LocalCorners;
            [NativeDisableUnsafePtrRestriction][ReadOnly] public float3* ViewCorners;
            [ReadOnly] public float3 Padding;
            [ReadOnly] public float Distance;
            [ReadOnly] public float NearClipOverride;
            
            [NativeDisableParallelForRestriction] 
            public NativeList&amp;lt;OrthoViewProjData&amp;gt; OrthoViewProjDatas;
            
            public unsafe void Execute(int index)
            {
                OrthoViewProjData data = OrthoViewProjDatas[index];
                CalculateOrthoViewProjection(ref data);
            }
            
            private unsafe void CalculateOrthoViewProjection(ref OrthoViewProjData data)
            {
                float3 extentsWithPadding = new float3(data.BoundsExtents + Padding);
                float3 min = new float3(data.BoundsCenter - extentsWithPadding);
                float3 max = new float3(data.BoundsCenter + extentsWithPadding);
           
                LocalCorners[0] = min.xyz;
                LocalCorners[1] = new float3(min.x, min.y, max.z);
                LocalCorners[2] = new float3(min.x, max.y, min.z);
                LocalCorners[3] = new float3(min.x, max.y, max.z);
                LocalCorners[4] = new float3(max.x, min.y, min.z);
                LocalCorners[5] = new float3(max.x, min.y, max.z);
                LocalCorners[6] = new float3(max.x, max.y, min.z);
                LocalCorners[7] = max.xyz;
        
                float3 localFrontCenter = new float3(data.BoundsCenter.x, data.BoundsCenter.y, max.z);
                float4 localFrontCenter4 = new float4(localFrontCenter, 1.0f);
                float4 worldFrontCenter4 = math.mul(data.LocalToWorldMatrix, localFrontCenter4);
                float3 worldFrontCenter = worldFrontCenter4.xyz / worldFrontCenter4.w;
                
                float3 objForward = math.normalize(math.mul(data.LocalToWorldMatrix, new float4(0, 0, 1, 0)).xyz);
                float3 objUp = math.normalize(math.mul(data.LocalToWorldMatrix, new float4(0, 1, 0, 0)).xyz);
                float3 eye = worldFrontCenter - objForward * Distance;
                float4x4 viewMatrix = float4x4.LookAt(eye, worldFrontCenter, objUp);
              
                for (int i = 0; i &amp;lt; 8; i++)
                {
                    float4 localCorner4 = new float4(LocalCorners[i], 1.0f);
                    float4 worldCorner4 = math.mul(data.LocalToWorldMatrix, localCorner4);
                    float3 worldCorner = worldCorner4.xyz / worldCorner4.w;
                    float4 viewCorner4 = math.mul(viewMatrix, new float4(worldCorner, 1.0f));
                    ViewCorners[i] = viewCorner4.xyz / viewCorner4.w;
                }
                
                float left = float.MaxValue;
                float right = float.MinValue;
                float bottom = float.MaxValue;
                float top = float.MinValue;
                float viewMaxZ = float.NegativeInfinity;
                float viewMinZ = float.PositiveInfinity;
                for (int i = 0; i &amp;lt; 8; i++)
                {
                    left = math.min(left, ViewCorners[i].x);
                    right = math.max(right, ViewCorners[i].x);
                    bottom = math.min(bottom, ViewCorners[i].y);
                    top = math.max(top, ViewCorners[i].y);
                    viewMaxZ = math.max(viewMaxZ, ViewCorners[i].z);
                    viewMinZ = math.min(viewMinZ, ViewCorners[i].z);
                }
                
                float nearPlane = (viewMaxZ &amp;lt; 0.0f) ? -viewMaxZ : NearClipOverride;
                float farPlane = (viewMinZ &amp;lt; 0.0f) ? -viewMinZ : NearClipOverride + 1.0f;
                /*float4x4 projMatrix = new float4x4(
                    new float4(2f / (right - left), 0f, 0f, 0f),
                    new float4(0f, 2f / (top - bottom), 0f, 0f),
                    new float4(0f, 0f, -2f / (farPlane - nearPlane), 0f),//
                    new float4(-(right + left) / (right - left), -(top + bottom) / (top - bottom), 
                              -(farPlane + nearPlane) / (farPlane - nearPlane), 1f));*/
                float4x4 projMatrix = new float4x4(
                    new float4(2f / (right - left), 0f, 0f, 0f),
                    new float4(0f, 2f / (top - bottom), 0f, 0f),
                    new float4(0f, 0f, 2f / (farPlane - nearPlane), 0f), 
                    new float4(-(right + left) / (right - left), -(top + bottom) / (top - bottom), 
                        (farPlane + nearPlane) / (farPlane - nearPlane), 1f)); 
                
                *data.ViewMatrix = viewMatrix;
                *data.ProjMatrix = projMatrix;
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 구현을 통해 PerObjectRT Renderer 객체는 실시간으로 Renderer의 Bounds 가 변경되어도 Local Bounds 및 ViewProjection 행렬 재구성이 빠른 속도로 가능하며, 대량의 오브젝트 대응도 가능하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GcUb4/btsPkFL1GRV/bxhByy6nG5W2DAZnwKiwC1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GcUb4/btsPkFL1GRV/bxhByy6nG5W2DAZnwKiwC1/img.gif&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;368&quot; data-is-animation=&quot;true&quot; data-filename=&quot;ZCZxc11122.gif&quot; data-widthpercent=&quot;45.53&quot; style=&quot;width: 45.002%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GcUb4/btsPkFL1GRV/bxhByy6nG5W2DAZnwKiwC1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGcUb4%2FbtsPkFL1GRV%2FbxhByy6nG5W2DAZnwKiwC1%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coVnXW/btsPlcJmh0M/sYB8yV1KNIccKm2fM0Odgk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coVnXW/btsPlcJmh0M/sYB8yV1KNIccKm2fM0Odgk/img.gif&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;323&quot; data-is-animation=&quot;true&quot; data-filename=&quot;ZCZxc111222.gif&quot; style=&quot;width: 53.8352%;&quot; data-widthpercent=&quot;54.47&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coVnXW/btsPlcJmh0M/sYB8yV1KNIccKm2fM0Odgk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoVnXW%2FbtsPlcJmh0M%2FsYB8yV1KNIccKm2fM0Odgk%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;323&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Spine &amp;amp; SkinnedMeshRenderer의 애니메이션에 맞게 동적 Bounds 조절 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZCZxc111222334.gif&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;291&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pOe4g/btsPjQ8DAC8/gR17PFllXRrr7dQNqvQGyK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pOe4g/btsPjQ8DAC8/gR17PFllXRrr7dQNqvQGyK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pOe4g/btsPjQ8DAC8/gR17PFllXRrr7dQNqvQGyK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/pOe4g/btsPjQ8DAC8/gR17PFllXRrr7dQNqvQGyK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;338&quot; data-filename=&quot;ZCZxc111222334.gif&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;291&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;RenderTexture &lt;/b&gt;&lt;b&gt;Atlas&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;구성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PerObjectRT 시스템에서 렌더링하는 RenderTexture는 PerObjectRT Renderer 갯수에 따라 동적으로 Atlas 사이즈를 조절합니다. Atlas는 단순하게 Slice(객체 1개) 크기와 최대 RT 사이즈를 지정해서 사이즈 변경이 필요하면 RT 객체 재생성하는 방식으로 제작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후에 Rectangle Packing과 같은 알고리즘을 활용해서 고정된 사각형 Slice가 아닌 적절하게 Atlas 빈공간에 쑤셔 들어가는 방식으로 개선 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고 : &lt;a href=&quot;https://github.com/ThomasMiz/RectpackSharp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/ThomasMiz/RectpackSharp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1752582153424&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;internal static void CalculateSliceData(int sliceCount, out int slicesPerRow, out Vector2 scaledSliceSize, out Vector2Int atlasResolution)
        {
            if (sliceCount &amp;lt; 1)
            {
                slicesPerRow = 0;
                scaledSliceSize = Vector2.zero;
                atlasResolution = Vector2Int.one;
                return;
            }

            Profiler.BeginSample(ProfilerStrings.CalculateSliceData);
            
            Vector2Int sliceResolution = SliceResolution;
            Vector2Int maxAtlasResolution = AtlasResolution;

            int maxSlicesPerRow = maxAtlasResolution.x / sliceResolution.x;
            int maxRows = maxAtlasResolution.y / sliceResolution.y;
            int maxAllowedSlices = maxSlicesPerRow * maxRows;

            // Slice 개수가 최대치를 초과하는 경우 Slice 크기를 축소
            scaledSliceSize = sliceResolution;
            if (sliceCount &amp;gt; maxAllowedSlices)
            {
                float uvScale = Mathf.Sqrt((float)maxAllowedSlices / sliceCount);
                scaledSliceSize.x = sliceResolution.x * uvScale;
                scaledSliceSize.y = sliceResolution.y * uvScale;
            }
            slicesPerRow = Mathf.FloorToInt(maxAtlasResolution.x / scaledSliceSize.x);
            int rowsNeeded = Mathf.CeilToInt((float)sliceCount / slicesPerRow);

            // Atlas 크기 계산 (Slice 축소가 없다면 최적의 크기, 축소가 있다면 최대 크기 유지)
            atlasResolution = sliceCount &amp;gt; maxAllowedSlices
                ? maxAtlasResolution
                : new Vector2Int(
                    Mathf.CeilToInt(Mathf.Min(sliceCount, slicesPerRow) * scaledSliceSize.x),
                    Mathf.CeilToInt(rowsNeeded * scaledSliceSize.y)
                );
            
            Profiler.EndSample();
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PerObjectRT Renderer 2개 &amp;amp; 3개일때 자동으로 Atlas 사이즈 조절.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;401&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dn09tM/btsPkGjPOgK/Z35HTImFiSCbhKLpapKiOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dn09tM/btsPkGjPOgK/Z35HTImFiSCbhKLpapKiOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dn09tM/btsPkGjPOgK/Z35HTImFiSCbhKLpapKiOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdn09tM%2FbtsPkGjPOgK%2FZ35HTImFiSCbhKLpapKiOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;968&quot; height=&quot;401&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;401&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nVepa/btsPlhjzJWD/OkKUhfkAw1kBYidowJKy7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nVepa/btsPlhjzJWD/OkKUhfkAw1kBYidowJKy7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nVepa/btsPlhjzJWD/OkKUhfkAw1kBYidowJKy7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnVepa%2FbtsPlhjzJWD%2FOkKUhfkAw1kBYidowJKy7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;942&quot; height=&quot;398&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UGUI에서는 단일 Atlas Texture를 사용하기 때문에 배칭 조건만 된다면 1 드로우콜로 일괄 렌더링 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZCZxc1112223341.gif&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VyOx0/btsPkrN0wBI/NZoKcrNzdL9G93rSuCYW5K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VyOx0/btsPkrN0wBI/NZoKcrNzdL9G93rSuCYW5K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VyOx0/btsPkrN0wBI/NZoKcrNzdL9G93rSuCYW5K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/VyOx0/btsPkrN0wBI/NZoKcrNzdL9G93rSuCYW5K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;378&quot; data-filename=&quot;ZCZxc1112223341.gif&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;521&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Tool 사용성 &amp;amp; API&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;툴 사용성은 매우 단순하게 디자인했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UGUI에 PerObjectRT Image 컴포넌트 추가해서 PerObjectRT Renderer 컴포넌트를 참조하면 해당 UI Image에 참조한 객체가 렌더링됩니다. 둘 중 하나가 비활성화하면 렌더링되지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwEYy3/btsPjfnt3MR/twKG2fUVK7siMs4jcQnYf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwEYy3/btsPjfnt3MR/twKG2fUVK7siMs4jcQnYf0/img.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;518&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.3882%; margin-right: 10px;&quot; data-widthpercent=&quot;55.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwEYy3/btsPjfnt3MR/twKG2fUVK7siMs4jcQnYf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwEYy3%2FbtsPjfnt3MR%2FtwKG2fUVK7siMs4jcQnYf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;916&quot; height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AQruO/btsPj5xHFRE/DtGIQgryenukKUkQOJxrT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AQruO/btsPj5xHFRE/DtGIQgryenukKUkQOJxrT0/img.png&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;602&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.449%;&quot; data-widthpercent=&quot;44.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AQruO/btsPj5xHFRE/DtGIQgryenukKUkQOJxrT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAQruO%2FbtsPj5xHFRE%2FDtGIQgryenukKUkQOJxrT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;870&quot; height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 스크립트에서 런타임 도중 객체 추가 &amp;amp; 삭제 관리 가능하도록 주요 함수는 public으로 선언되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;1018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4h1Q9/btsPk4Y4iW3/P0HKFOSlGRq5WLFKvxN5Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4h1Q9/btsPk4Y4iW3/P0HKFOSlGRq5WLFKvxN5Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4h1Q9/btsPk4Y4iW3/P0HKFOSlGRq5WLFKvxN5Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4h1Q9%2FbtsPk4Y4iW3%2FP0HKFOSlGRq5WLFKvxN5Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;749&quot; height=&quot;781&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;1018&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;한계점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PerObjectRT 시스템은 목적에 맞게 개발되어, 실제 프로젝트에 사용되고 있습니다. (프로젝트는 추후 공개)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 몇가지 큰 한계점이 존재합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;SRP Batcher 미지원.&lt;/li&gt;
&lt;li&gt;실시간 라이팅 및 UnityPerDraw 상수 버퍼 관련 기능 미지원.&lt;/li&gt;
&lt;li&gt;Animator 및 ParticleSystem Camera Culling 미지원.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 한계점은 전부 Unity &amp;amp; URP 내장 시스템에서 동작하는 기능으로, 커맨드버퍼로 직접 렌더링하는 시스템 특성상 해당 기능을 직접 구현하지 않으면 극복 불가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 기능들을 하나같이 다 규모가 큰 작업으로, 차라리 성능 낭비를 좀 하더라도 URP 렌더파이프라인 기반으로 다시 만드는게 더 나을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>burst</category>
      <category>job</category>
      <category>nativearray</category>
      <category>particle system ugui</category>
      <category>perobject to rendertexture</category>
      <category>spine ugui</category>
      <category>unity</category>
      <category>unsafe</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/192</guid>
      <comments>https://darkcatgame.tistory.com/192#entry192comment</comments>
      <pubDate>Tue, 15 Jul 2025 21:59:52 +0900</pubDate>
    </item>
    <item>
      <title>Unity6 Dynamic Resolution 소개 및 대응 방법</title>
      <link>https://darkcatgame.tistory.com/191</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWaIg4/btsPcjqhbU3/HTquSqkjzBkKkHJXorKKt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWaIg4/btsPcjqhbU3/HTquSqkjzBkKkHJXorKKt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWaIg4/btsPcjqhbU3/HTquSqkjzBkKkHJXorKKt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWaIg4%2FbtsPcjqhbU3%2FHTquSqkjzBkKkHJXorKKt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;877&quot; height=&quot;476&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dynamic Resolution은 런타임 중에 렌더링 해상도를 동적으로 제어하는 시스템입니다.&lt;/li&gt;
&lt;li&gt;프레임 버퍼 해상도는 유지하고 렌더링하는 영역만 조정하는 동작 방식이며 GPU 픽셀 연산과 대역폭 최적화 효과를 얻을 수 있습니다.&lt;/li&gt;
&lt;li&gt;Unity6에서 동작하며, 그 이전 버전에서 옵션은 존재했지만 기본적으로 동작하지 않았습니다.&lt;/li&gt;
&lt;li&gt;렌더 패스 및 프레임 버퍼를 활용하는 기능을 개발한다면 Dynamic Resolution 지원하도록 구현해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dynamic Resolution 기능 사용 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity6 환경 기준, Camera 컴포넌트에 &lt;b&gt;&quot;UPR Dynamic Resolution&quot;&lt;/b&gt; 옵션을 활성화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity6 이전 버전에서는 &lt;b&gt;&quot;Allow Dynamic Resolution&quot;&lt;/b&gt; 옵션으로 존재했지만, Unity 2022.3.38f1 버전 기준으로 활성화해도 동작하지 않고 URP 내장 Shader 및 RenderPass에서 기능을 지원하도록 구현되어 있지도 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/norYa/btsPc5LEcpM/Ma2gu6gedojBflwUz7HyCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/norYa/btsPc5LEcpM/Ma2gu6gedojBflwUz7HyCK/img.png&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;456&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.7903%; margin-right: 10px;&quot; data-widthpercent=&quot;50.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/norYa/btsPc5LEcpM/Ma2gu6gedojBflwUz7HyCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnorYa%2FbtsPc5LEcpM%2FMa2gu6gedojBflwUz7HyCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1160&quot; height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MiTm2/btsPead7ZEc/47lsi4Kzh8LoGU0vwLHkm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MiTm2/btsPead7ZEc/47lsi4Kzh8LoGU0vwLHkm0/img.png&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;340&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0469%;&quot; data-widthpercent=&quot;49.62&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MiTm2/btsPead7ZEc/47lsi4Kzh8LoGU0vwLHkm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMiTm2%2FbtsPead7ZEc%2F47lsi4Kzh8LoGU0vwLHkm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;852&quot; height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : Unity6000.0.32f1 우 : Unity 2022.3.38f1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic Resolution 해상도 제어는 스크립트에서 &lt;b&gt;&quot;ScalableBufferManager&quot; 클래스&lt;/b&gt;를 통해서 제어할 수 있으며 일반적으로 디바이스 성능 정보를 기준으로 런타임에서 동적으로 조정되도록 구현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 유니티 도큐먼트 페이지에서 제공하는 샘플 스크립트입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.unity3d.com/6000.1/Documentation/Manual/DynamicResolution-control.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.unity3d.com/6000.1/Documentation/Manual/DynamicResolution-control.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Dynamic Resolution 동작 원리와 최적화 이득&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 상태&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FnIvY/btsPcTYPAJt/HCtLxcmdyZw2jA8kQF2hY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FnIvY/btsPcTYPAJt/HCtLxcmdyZw2jA8kQF2hY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FnIvY/btsPcTYPAJt/HCtLxcmdyZw2jA8kQF2hY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFnIvY%2FbtsPcTYPAJt%2FHCtLxcmdyZw2jA8kQF2hY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;887&quot; height=&quot;450&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dynamic Resolution 적용&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqGQiv/btsPcfBgO2P/KuZRbkKLaCQDiiPsREu4G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqGQiv/btsPcfBgO2P/KuZRbkKLaCQDiiPsREu4G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqGQiv/btsPcfBgO2P/KuZRbkKLaCQDiiPsREu4G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqGQiv%2FbtsPcfBgO2P%2FKuZRbkKLaCQDiiPsREu4G0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;892&quot; height=&quot;450&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic Resolution을 통해서 렌더링 해상도를 제어하면 위 예시와 같이, &lt;b&gt;프레임 버퍼(렌더 텍스처) 해상도는 유지하고 렌더링되는 영역만 조정&lt;/b&gt;합니다. 이로서 시스템 메모리에 점유하는 렌더 텍스처 용량은 변하지 않지만, &lt;b&gt;GPU 픽셀 연산과 대역폭 최적화&lt;/b&gt; 효과를 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Render Scale &amp;amp; Screen.SetResolution 옵션과 차이점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic Resolution 외에 렌더 해상도 조정 옵션은 추가로 Render Scale &amp;amp; Screen.SetResolution 기능이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Render Scale 옵션은 실제 프레임 버퍼 (렌더 텍스처) 해상도 자체를 조정하는 옵션이며 Screen.SetResolution은 어플리케이션 해상도 자체를 조정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Screen.SetResolution과 용도 차이는 RenderScale은 Camera 렌더링 단계에서 프레임 버퍼 해상도를 조정해서 Final Blit 단계에서 다시 원본 해상도로 복구해서 UI를 해상도 조정 영향 받지 않고 선명하게 렌더링 가능하다는 점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Screen Space - Overlay UI만 해당됨)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qahPu/btsPb318yYm/m5i6ablqxYfx52AuxvFJCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qahPu/btsPb318yYm/m5i6ablqxYfx52AuxvFJCK/img.png&quot; data-alt=&quot;Render Scale 옵션은 URP Renderer Asset 혹은 스크립트에서 제어 가능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qahPu/btsPb318yYm/m5i6ablqxYfx52AuxvFJCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqahPu%2FbtsPb318yYm%2Fm5i6ablqxYfx52AuxvFJCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;169&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Render Scale 옵션은 URP Renderer Asset 혹은 스크립트에서 제어 가능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Render Scale &amp;amp; Screen.RetResolution 또한 런타임 도중 동적으로 제어 가능하지만 Dynamic Resolution과 다르게 1 프레임 단위로 렌더링 해상도 제어하는 용도가 아닙니다. 왜냐하면 프레임 버퍼 해상도가 변경되면 한 프레임 렌더링에 필요한 모든 렌더 텍스처를 새로 생성해야 하기 때문에 해상도 변경 순간 프레임 히칭 및 GC 발생하여 성능 부하 발생하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(원래 렌더 텍스처를 한번 생성해 놓고 재활용하는 방식으로 동작함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;Dynamic Resolution은 Adaptive Performance와 같이 디바이스 온도나 프레임 레이트 정보를 기반으로 프레임 단위로 렌더링 해상도 제어하는 용도로 사용&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 Dynamic Resolution 또한 Final Blit 단계에서 원본 해상도로 복구하기 때문에 Screen Space - Overlay UI 해상도에 영향주지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Dynamic Resolution Shader &amp;amp; RenderPass 지원 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity6 버전으로 오면서 내장 Shader 및 Blitter 클래스의 코드가 많이 변화되었으며, 그 중에서 아래와 같이 이름만 봐도 Dynamic Resolution과 연관되어 보이는 변수 &amp;amp; 함수들이 확인됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4JrbI/btsPd4kHQ5Q/jjAsdiIbN73KSA5QnrZLN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4JrbI/btsPd4kHQ5Q/jjAsdiIbN73KSA5QnrZLN0/img.png&quot; data-alt=&quot;Unity 6000.032f1 - Blit.hlsl&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4JrbI/btsPd4kHQ5Q/jjAsdiIbN73KSA5QnrZLN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4JrbI%2FbtsPd4kHQ5Q%2FjjAsdiIbN73KSA5QnrZLN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;925&quot; height=&quot;352&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Unity 6000.032f1 - Blit.hlsl&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity 6000.1.9f1 기준, 이와 관련해서 내장 Shader 및 구현 로직을 확인해본 결과.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 UV 처리 관련 코드는 Blitter 클래스 및 Blit.hlsl와 같은 편의성 헬퍼 클래스를 위한 기능으로 확인되며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 꽉차게 그리는 렌더패스 스타일 기준으로 Dynamic Resolution은 애초에 Viewport 자체를 화면 버퍼보다 작게 세팅되기 때문에 별도 대응할 필요 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(혹시 제가 모르는 무언가가 있다면 댓글로 알려주시면 감사드립니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Bloom &amp;amp; DoF와 같은 다운샘플링 &amp;amp; 블러 처리하는 효과 구현에서는 보통 _BlitTexture_TexelSize와 같은 RenderTexture 사이즈와 텍셀 정보 데이터를 이용하게되는데, Dynamic Resolution을 사용하는 경우, 실제 Viewport 사이즈만 조절되기 때문에 이에 대한 대응은 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Unity 6000.0.32f1 기준, URP Bloom이 이 대응이 안되있어서 Dynamic Resolution으로 해상도 낮추면 Blur가 더 퍼짐.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostProcessUtils.SetSourceSize 함수를 이용한다면, 내부에서 DynamicResolution의 해상도 값을 통해 _SourceSize라는 변수 명으로 셰이더 글로벌 상수 버퍼로 전달할 수 있습니다. _BlitTexture_TexelSize를 _SourceSize 로 대체하면 다운 샘플링 &amp;amp; 블러 처리에 Dynamic Resolution 대응 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1265&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xHT32/btsPb5yV7FQ/izW1QBhocSlsGkF5KNcHqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xHT32/btsPb5yV7FQ/izW1QBhocSlsGkF5KNcHqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xHT32/btsPb5yV7FQ/izW1QBhocSlsGkF5KNcHqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxHT32%2FbtsPb5yV7FQ%2FizW1QBhocSlsGkF5KNcHqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1265&quot; height=&quot;223&quot; data-origin-width=&quot;1265&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQb9O/btsPlcYubCa/aTxvu1KybcKsrT0NALvDi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQb9O/btsPlcYubCa/aTxvu1KybcKsrT0NALvDi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQb9O/btsPlcYubCa/aTxvu1KybcKsrT0NALvDi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQb9O%2FbtsPlcYubCa%2FaTxvu1KybcKsrT0NALvDi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1073&quot; height=&quot;279&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;279&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외로 Dynamic Resolution은 _GlobalMipBias에도 영향을 주는데, 조정된 렌더 해상도에 알맞는 MipBias가 설정되기 위함으로 보입니다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;일반적인 화면 효과 RenderTexture에는 Mipmap을 생성하지 않아서 신경 안써도 되지만, RenderObject Pass를 제작하는 경우에는 신경써야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Dynamic Resolution으로 절반 해상도로 만들면 MipBias를 -1.0로 Offset 시키는 원리)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n5fff/btsPk4lVrPr/bPJVdn038u9bj4rOyTx9a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n5fff/btsPk4lVrPr/bPJVdn038u9bj4rOyTx9a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n5fff/btsPk4lVrPr/bPJVdn038u9bj4rOyTx9a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn5fff%2FbtsPk4lVrPr%2FbPJVdn038u9bj4rOyTx9a0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;920&quot; height=&quot;93&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>adaptive performance</category>
      <category>dynamic resolution</category>
      <category>unity6</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/191</guid>
      <comments>https://darkcatgame.tistory.com/191#entry191comment</comments>
      <pubDate>Thu, 10 Jul 2025 22:28:26 +0900</pubDate>
    </item>
    <item>
      <title>Unity RenderBufferLoadAction &amp;amp; StoreAction 옵션 설명</title>
      <link>https://darkcatgame.tistory.com/190</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity 엔진에서 ScriptableRenderPass 등, 커맨드 버퍼에 RenderTarget 설정 할때 RenderBufferLoadAction &amp;amp; StoreAction 옵션에 대한 설명입니다. 해당 옵션은 SetRenderTarget으로 설정한 렌더버퍼에 대한 데이터를 메모리에 Load 옵션과 Store(방출) 옵션을 설정하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션에 따라 메모리 대역폭에 영향을 주기 때문에 구현하는 RenderPass 목적에 따라 알맞게 설정하면 최적화에 도움됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lIEUp/btsOwNDQC8J/DQMyaGXK7npX3Ipne6op7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lIEUp/btsOwNDQC8J/DQMyaGXK7npX3Ipne6op7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lIEUp/btsOwNDQC8J/DQMyaGXK7npX3Ipne6op7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlIEUp%2FbtsOwNDQC8J%2FDQMyaGXK7npX3Ipne6op7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;972&quot; height=&quot;183&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;옵션 설명&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;RenderBufferLoadAction 옵션&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;LoadAction.Load
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 : GPU가 렌더링 할 때 기존 프레임 버퍼 내용을 로드합니다.&lt;/li&gt;
&lt;li&gt;용도 : Blend 처리와 같이 현재 버퍼 내용과 이후 드로우콜 결과물이 합쳐지는 용도로 사용합니다.&lt;/li&gt;
&lt;li&gt;성능 고려 사항 : 메모리에서 전체 버퍼를 읽어야 하기 때문에 대역폭 비용이 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LoadAction.DontCare
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 : GPU가 렌더링 할 때 기존 프레임 버퍼 내용을 로드하지 않습니다.&lt;/li&gt;
&lt;li&gt;용도 : 프레임 버퍼 내용을 덮어 쓸 때 사용합니다. (Blend는 안되지만 샘플링은 가능합니다.)&lt;/li&gt;
&lt;li&gt;성능 고려 사항 : 가장 최적화된 옵션이며, 메모리 대역폭 절약합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LoadAction.Clear
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 : 프레임 버퍼 내용을 지정된 색상으로 초기화합니다, 깊이 버퍼 경우에는 흰색(1.0)으로 초기화됩니다.&lt;/li&gt;
&lt;li&gt;용도 : 프레임 버퍼를 지우고 새로 그릴 때 사용합니다. 그런데 cmd.ClearRenderTarget 메소드로 동일한 처리 가능해서 왜 있는 옵션인지 모르겠습니다.&lt;/li&gt;
&lt;li&gt;성능 고려 사항 : 메뉴얼 상에는 Clear 이후 LoadAction.Load로 동작한다고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;RenderBufferStoreAction 옵션&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;StoreAction.Store
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 : 렌더링 결과를 메모리에 저장합니다.&lt;/li&gt;
&lt;li&gt;용도 : 이 렌더 타겟을 다시 사용하거나 화면에 표시할 때 사용합니다. (화면에 그려야 하거나 다음 패스에서 샘플링 필요하면 사용)&lt;/li&gt;
&lt;li&gt;성능 고려 사항 : 전체 렌더 타겟을 메모리에 쓰므로 대역폭 비용이 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;StoreAction.DontCare
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 : 렌더링 결과를 메모리에 저장하지 않습니다.&lt;/li&gt;
&lt;li&gt;용도 : 일회성 계산이나 중간 결과물로 더 이상 필요하지 않은 렌더 타겟에 사용합니다.&lt;/li&gt;
&lt;li&gt;성능 고려 사항 : 메모리 쓰기 작업을 수행하지 않으므로 메모리 대역폭을 절약합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;StoreAction.Resolve
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 : 멀티샘플 버퍼의 여러 샘플을 통합하여 단일 샘플 텍스처로 변환합니다, 원본 멀티 샘플 버퍼 정보는 유지하지 않습니다.&lt;/li&gt;
&lt;li&gt;용도 : MSAA 렌더링 후 결과를 샘플링해야 할 때 필요합니다.&lt;/li&gt;
&lt;li&gt;성능 고려 사항 : 원본 멀티샘플 데이터는 버리므로 메모리 사용량이 줄어듭니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;StoreAction.StoreAndResolve
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능 : 멀티샘플 버퍼를 원본 형태로 저장하고, 동시에 통합된 단일 샘플 버전도 생성합니다.&lt;/li&gt;
&lt;li&gt;용도 : 멀티샘플 데이터를 계속 유지하면서 동시에 후처리를 위한 단일 샘플 버전이 필요할 때 사용합니다.&lt;/li&gt;
&lt;li&gt;성능 고려 사항 : 두 형태의 데이터를 모두 저장하므로 메모리 사용량이 두 배로 증가할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;만약 올바르지 않게 설정하면 발생하는 이슈&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이슈 재현 환경은 LoadAction을 DontCare로 설정하고 Blend 셰이더 드로우콜 호출하게 세팅했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blend 셰이더가 동작하기 위해서 RenderTarget의 프레임 버퍼 픽셀을 정보가 필요하기 때문에 LoadAction을 Load로 세팅하는 것이 올바른 것 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1561&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AhsyL/btsOuLN4PKb/HmMNKn5HzuIftomJ6jNQK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AhsyL/btsOuLN4PKb/HmMNKn5HzuIftomJ6jNQK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AhsyL/btsOuLN4PKb/HmMNKn5HzuIftomJ6jNQK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAhsyL%2FbtsOuLN4PKb%2FHmMNKn5HzuIftomJ6jNQK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1561&quot; height=&quot;290&quot; data-origin-width=&quot;1561&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6ttU1/btsOuKIio3F/szrsiDbqJYrse5rnPYLWsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6ttU1/btsOuKIio3F/szrsiDbqJYrse5rnPYLWsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6ttU1/btsOuKIio3F/szrsiDbqJYrse5rnPYLWsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6ttU1%2FbtsOuKIio3F%2FszrsiDbqJYrse5rnPYLWsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;347&quot; height=&quot;56&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity 2022 &amp;amp; 6000.032 버전 기준으로 에디터에서는 문제 없이 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 디바이스 빌드에서 발생하며 안드로이드 기준, GPU 칩셋 종류와 Graphics API에 따라 문제 현상이 다르게 발생합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유니티 에디터&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;627&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vClgl/btsOwsmmfYp/1ZFTlov1rrvDsWlTUECMWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vClgl/btsOwsmmfYp/1ZFTlov1rrvDsWlTUECMWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vClgl/btsOwsmmfYp/1ZFTlov1rrvDsWlTUECMWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvClgl%2FbtsOwsmmfYp%2F1ZFTlov1rrvDsWlTUECMWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1468&quot; height=&quot;627&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;627&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Adreno 750(갤럭시z플립6) / Vulkan&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2173&quot; data-origin-height=&quot;1088&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7UH0P/btsOvUw1iNw/IhPL7xXAgKUYYpHmaVSxH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7UH0P/btsOvUw1iNw/IhPL7xXAgKUYYpHmaVSxH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7UH0P/btsOvUw1iNw/IhPL7xXAgKUYYpHmaVSxH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7UH0P%2FbtsOvUw1iNw%2FIhPL7xXAgKUYYpHmaVSxH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2173&quot; height=&quot;1088&quot; data-origin-width=&quot;2173&quot; data-origin-height=&quot;1088&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Adreno 750(갤럭시z플립6) / OpenGLES&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVhOjC/btsOvI4GWgo/IjB6H05fE8SX7siDPHjXH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVhOjC/btsOvI4GWgo/IjB6H05fE8SX7siDPHjXH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVhOjC/btsOvI4GWgo/IjB6H05fE8SX7siDPHjXH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVhOjC%2FbtsOvI4GWgo%2FIjB6H05fE8SX7siDPHjXH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2200&quot; height=&quot;1113&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mali-G76(갤럭시s10e) / Vulkan&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2214&quot; data-origin-height=&quot;1077&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/33QZf/btsOusut6xA/UzKb0pYvCQR2OHm5dQRLS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/33QZf/btsOusut6xA/UzKb0pYvCQR2OHm5dQRLS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/33QZf/btsOusut6xA/UzKb0pYvCQR2OHm5dQRLS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F33QZf%2FbtsOusut6xA%2FUzKb0pYvCQR2OHm5dQRLS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2214&quot; height=&quot;1077&quot; data-origin-width=&quot;2214&quot; data-origin-height=&quot;1077&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mali-G76(갤럭시s10e) / OpenGLES&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;1105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqjAf0/btsOvfhklSE/j0hWVvzeuGmEfZJDclL9t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqjAf0/btsOvfhklSE/j0hWVvzeuGmEfZJDclL9t0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqjAf0/btsOvfhklSE/j0hWVvzeuGmEfZJDclL9t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqjAf0%2FbtsOvfhklSE%2Fj0hWVvzeuGmEfZJDclL9t0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2080&quot; height=&quot;1105&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;1105&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과와 같이 SetRenderTarget 메소드의 RenderBufferLoad &amp;amp; Store 세팅을 올바르지 않게 하면 디바이스 환경에 따라 문제 발생하니 RenderPass 구현 용도에 알맞게 세팅 필요합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 59px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;Vulkan&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;OpenGLES&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt; Adreno 750(갤럭시z플립6) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 21px;&quot;&gt;정상&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 21px;&quot;&gt;비정상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 21px;&quot;&gt;&lt;b&gt; Mali-G76(갤럭시s10e) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 21px;&quot;&gt;비정상&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 21px;&quot;&gt;정상&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 문제되는 환경 세팅에서 RenderBufferLoad 옵션을 올바르게 세팅하는 경우 정상 동작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2212&quot; data-origin-height=&quot;1059&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z6EMa/btsOu6LahLk/Gm6CLiGUWAL0rXIJNa5PGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z6EMa/btsOu6LahLk/Gm6CLiGUWAL0rXIJNa5PGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z6EMa/btsOu6LahLk/Gm6CLiGUWAL0rXIJNa5PGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ6EMa%2FbtsOu6LahLk%2FGm6CLiGUWAL0rXIJNa5PGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2212&quot; height=&quot;1059&quot; data-origin-width=&quot;2212&quot; data-origin-height=&quot;1059&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 설명을 반복해서 RenderBufferLoad &amp;amp; Store 옵션은 용도에 알맞게 세팅해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올바르지 않은 세팅에서 정상 동작되는 상황은 자세한 원인은 Xcode GPU FrameCapture와 같은 GPU 프로파일링 도구를 통해 확인해봐야 겠지만, API 단계에서 자동으로 처리하는 상황으로 보입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>renderbufferloadaction</category>
      <category>renderbufferstoreaction</category>
      <category>ScriptableRenderPass</category>
      <category>setrendertarget</category>
      <category>unity</category>
      <category>URP</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/190</guid>
      <comments>https://darkcatgame.tistory.com/190#entry190comment</comments>
      <pubDate>Wed, 11 Jun 2025 00:29:19 +0900</pubDate>
    </item>
    <item>
      <title>스캐니메이션 게임 엔진 테스트</title>
      <link>https://darkcatgame.tistory.com/189</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;asedqw111233.gif&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8GfvC/btsN7ZZgegA/Al1MKQOLaCUZK8nRiZFtq1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8GfvC/btsN7ZZgegA/Al1MKQOLaCUZK8nRiZFtq1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8GfvC/btsN7ZZgegA/Al1MKQOLaCUZK8nRiZFtq1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/8GfvC/btsN7ZZgegA/Al1MKQOLaCUZK8nRiZFtq1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;799&quot; height=&quot;387&quot; data-filename=&quot;asedqw111233.gif&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;asedqw1112.gif&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wMRiN/btsN8woLdJ1/bL2KusE0ak1sgdruNz0ftK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wMRiN/btsN8woLdJ1/bL2KusE0ak1sgdruNz0ftK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wMRiN/btsN8woLdJ1/bL2KusE0ak1sgdruNz0ftK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/wMRiN/btsN8woLdJ1/bL2KusE0ak1sgdruNz0ftK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;799&quot; height=&quot;387&quot; data-filename=&quot;asedqw1112.gif&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Misc</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/189</guid>
      <comments>https://darkcatgame.tistory.com/189#entry189comment</comments>
      <pubDate>Wed, 21 May 2025 22:54:19 +0900</pubDate>
    </item>
    <item>
      <title>Unity Cubemap Shadow</title>
      <link>https://darkcatgame.tistory.com/188</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1459&quot; data-origin-height=&quot;687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6pq1i/btsNDY0Fohw/ECMwtHomnQaItIbdYI9QvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6pq1i/btsNDY0Fohw/ECMwtHomnQaItIbdYI9QvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6pq1i/btsNDY0Fohw/ECMwtHomnQaItIbdYI9QvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6pq1i%2FbtsNDY0Fohw%2FECMwtHomnQaItIbdYI9QvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1459&quot; height=&quot;687&quot; data-origin-width=&quot;1459&quot; data-origin-height=&quot;687&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFhLNp/btsNEnToukv/jWZkmhQmkjcIU7iLborqt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFhLNp/btsNEnToukv/jWZkmhQmkjcIU7iLborqt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFhLNp/btsNEnToukv/jWZkmhQmkjcIU7iLborqt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFhLNp%2FbtsNEnToukv%2FjWZkmhQmkjcIU7iLborqt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;840&quot; height=&quot;472&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDhBxv/btsND0D93fy/J9KKhGKkz3pZXykcfvTOTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDhBxv/btsND0D93fy/J9KKhGKkz3pZXykcfvTOTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDhBxv/btsND0D93fy/J9KKhGKkz3pZXykcfvTOTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDhBxv%2FbtsND0D93fy%2FJ9KKhGKkz3pZXykcfvTOTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;466&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cubemap Shadow는 Cubemap에 ShadowMask를 캡처해서 밀폐된 실내 공간에서 창문이나 외부 구멍과 같은 외부 조명 그림자를 표현 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 샘플은 실시간 그림자 없이 그림자 처리한 결과물입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://community.arm.com/arm-community-blogs/b/mobile-graphics-and-gaming-blog/posts/dynamic-soft-shadows-based-on-local-cubemap&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://community.arm.com/arm-community-blogs/b/mobile-graphics-and-gaming-blog/posts/dynamic-soft-shadows-based-on-local-cubemap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;구현 원리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Camera.Rendercubemap API를 통해 Cubemap을 생성하면, 알파채널에 캡처되지 않은 마스크 영역이 기록됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보를 그림자 마스크로 활용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnxK7H/btsNDn7YBuR/zKkFuvLADLv8nq6exYoTq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnxK7H/btsNDn7YBuR/zKkFuvLADLv8nq6exYoTq1/img.png&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;443&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.4105%; margin-right: 10px;&quot; data-widthpercent=&quot;53.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnxK7H/btsNDn7YBuR/zKkFuvLADLv8nq6exYoTq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnxK7H%2FbtsNDn7YBuR%2FzKkFuvLADLv8nq6exYoTq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;484&quot; height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDhBxv/btsND0D93fy/J9KKhGKkz3pZXykcfvTOTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDhBxv/btsND0D93fy/J9KKhGKkz3pZXykcfvTOTk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;466&quot; data-origin-width=&quot;451&quot; style=&quot;width: 46.4267%;&quot; data-widthpercent=&quot;46.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDhBxv/btsND0D93fy/J9KKhGKkz3pZXykcfvTOTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDhBxv%2FbtsND0D93fy%2FJ9KKhGKkz3pZXykcfvTOTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shader에서는 Cubemap 월드 픽셀 좌표와 라이트&amp;nbsp; 좌표의 방향 벡터 정보를 기반으로 Cubemap 샘플링합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SaCRq/btsNDw4F4eD/Lq4DR0Tuu8f7yjLQPBMq1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SaCRq/btsNDw4F4eD/Lq4DR0Tuu8f7yjLQPBMq1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SaCRq/btsNDw4F4eD/Lq4DR0Tuu8f7yjLQPBMq1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSaCRq%2FbtsNDw4F4eD%2FLq4DR0Tuu8f7yjLQPBMq1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;819&quot; height=&quot;724&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Htjr/btsNEcYHPTn/IbB01SoJ20kn4jtKZIyE4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Htjr/btsNEcYHPTn/IbB01SoJ20kn4jtKZIyE4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Htjr/btsNEcYHPTn/IbB01SoJ20kn4jtKZIyE4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Htjr%2FbtsNEcYHPTn%2FIbB01SoJ20kn4jtKZIyE4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;974&quot; height=&quot;646&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSM과 비교&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌 = CSM, 우 = CubemapShadow&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfF89R/btsNB9v2EnB/y0YAE28l3ngFU5EhsknElK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfF89R/btsNB9v2EnB/y0YAE28l3ngFU5EhsknElK/img.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;653&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.6528%; margin-right: 10px;&quot; data-widthpercent=&quot;50.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfF89R/btsNB9v2EnB/y0YAE28l3ngFU5EhsknElK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfF89R%2FbtsNB9v2EnB%2Fy0YAE28l3ngFU5EhsknElK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6pq1i/btsNDY0Fohw/ECMwtHomnQaItIbdYI9QvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6pq1i/btsNDY0Fohw/ECMwtHomnQaItIbdYI9QvK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;687&quot; data-origin-width=&quot;1459&quot; style=&quot;width: 49.1844%;&quot; data-widthpercent=&quot;49.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6pq1i/btsNDY0Fohw/ECMwtHomnQaItIbdYI9QvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6pq1i%2FbtsNDY0Fohw%2FECMwtHomnQaItIbdYI9QvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1459&quot; height=&quot;687&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/188</guid>
      <comments>https://darkcatgame.tistory.com/188#entry188comment</comments>
      <pubDate>Mon, 28 Apr 2025 20:46:10 +0900</pubDate>
    </item>
    <item>
      <title>Unity ShaderGraph Custom Lighting Model 활용 테스트</title>
      <link>https://darkcatgame.tistory.com/187</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 소개한 ShaderGraph Custom Lighting Model 활용 테스트 내용입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://darkcatgame.tistory.com/186&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://darkcatgame.tistory.com/186&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 자료를 참고해서 Universal Target을 유지하고, SubTarget만 커스텀으로 추가하는 방식으로 구현했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Zallist/unity.zallist.universal-simple-lit-shadergraph-target/tree/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Zallist/unity.zallist.universal-simple-lit-shadergraph-target/tree/main&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SubTarget 커스텀 추가를 통해 Skin, Hair 등 원하는 라이팅 모델을 ShaderGraph에서 사용할 수 있는 것이 가능하지만, Shader 코드 작성이 어렵고 엔진 API가 변동되면 에러 발생할 가능성이 매우 높습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;zxcasweqwe.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUN467/btsNlLoGG4h/hNClFoGV6MoAKJUrSambpK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUN467/btsNlLoGG4h/hNClFoGV6MoAKJUrSambpK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUN467/btsNlLoGG4h/hNClFoGV6MoAKJUrSambpK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bUN467/btsNlLoGG4h/hNClFoGV6MoAKJUrSambpK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;473&quot; data-filename=&quot;zxcasweqwe.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/olaMR/btsNkolX9SD/ja6nyohydlppS10mMFfguK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/olaMR/btsNkolX9SD/ja6nyohydlppS10mMFfguK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/olaMR/btsNkolX9SD/ja6nyohydlppS10mMFfguK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FolaMR%2FbtsNkolX9SD%2Fja6nyohydlppS10mMFfguK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;967&quot; height=&quot;625&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;625&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caGmDp/btsNmlQnf15/dPJnoNzGhJO3Nr6EZkFKAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caGmDp/btsNmlQnf15/dPJnoNzGhJO3Nr6EZkFKAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caGmDp/btsNmlQnf15/dPJnoNzGhJO3Nr6EZkFKAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaGmDp%2FbtsNmlQnf15%2FdPJnoNzGhJO3Nr6EZkFKAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1188&quot; height=&quot;666&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>custom lighting model</category>
      <category>shadergraph</category>
      <category>subtarget</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/187</guid>
      <comments>https://darkcatgame.tistory.com/187#entry187comment</comments>
      <pubDate>Tue, 15 Apr 2025 22:15:20 +0900</pubDate>
    </item>
    <item>
      <title>Unity ShaderGraph Custom Lighting Model 추가 방법</title>
      <link>https://darkcatgame.tistory.com/186</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity ShaderGraph에 Target 인터페이스를 기반으로 Custom Lighting Model 추가 방법을 소개합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity 6000.0.32f1 URP 환경에서 테스트 내용입니다.&lt;/li&gt;
&lt;li&gt;흔히 알려진 SubGraph 활용 방식이 아닌, ShaderGraph에서 직접 지원할 수 있게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1615&quot; data-origin-height=&quot;663&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpn73o/btsNgDQVbTk/k8p1XHsoj1xfe8t4GNVNUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpn73o/btsNgDQVbTk/k8p1XHsoj1xfe8t4GNVNUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpn73o/btsNgDQVbTk/k8p1XHsoj1xfe8t4GNVNUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcpn73o%2FbtsNgDQVbTk%2Fk8p1XHsoj1xfe8t4GNVNUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1615&quot; height=&quot;663&quot; data-origin-width=&quot;1615&quot; data-origin-height=&quot;663&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ShaderGraph에 Custom Lighting Model을 추가하고 싶은 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ShaderGraph 사용 장점은 &lt;b&gt;통일된 라이팅 모델 기반으로 필요한 Shader를 빠르게 제작 가능&lt;/b&gt;한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 ShaderGraph는 라이팅 모델 커스텀을 지원하지 않으며 URP Lit 등, 기본적으로 엔진에서 지원하고 있는 Lighting Model만 사용 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 PBR이 아닌 카툰 렌더링과 같이 특수한 라이팅 표현을 하고 싶거나 더 최적화된 PBR Lighting Model을 사용하고 싶다면, ShaderGraph를 사용하지 않고 HLSL 코드 기반으로 셰이더를 제작하거나, ShaderGraph에서 Unlit 기반으로 SubGraph를 활용해 어거지로 커스텀 라이팅 모델이 동작하도록 해야합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고 SubGraph Custom Lighting Model : &lt;a href=&quot;https://nedmakesgames.medium.com/creating-custom-lighting-in-unitys-shader-graph-with-universal-render-pipeline-5ad442c27276&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nedmakesgames.medium.com/creating-custom-lighting-in-unitys-shader-graph-with-universal-render-pipeline-5ad442c27276&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 프로젝트 맞춤형 Custom Lighting Model을 사용하며 ShaderGraph의 Shader 생산성을 얻고자 R&amp;amp;D 진행했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ShaderGraph Target 인터페이스 이용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ShaderGraph에 Target 설정 인터페이스가 있습니다, Target RenderPipline을 의미하는 것으로 추측되며 기본적으로 Universal(URP), Built-In, Custom RenderTexture 3가지 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ShaderGraph 패키지에 Universal을 제외한 다른 Target 구현 자료가 있어 해당 자료를 기반으로 Target을 추가하는 방식으로 진행합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMgQno/btsNfQi8VWC/1G0ekEEZdq8dPsi8kgaK7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMgQno/btsNfQi8VWC/1G0ekEEZdq8dPsi8kgaK7k/img.png&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;616&quot; data-is-animation=&quot;false&quot; style=&quot;width: 40.6064%; margin-right: 10px;&quot; data-widthpercent=&quot;41.08&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMgQno/btsNfQi8VWC/1G0ekEEZdq8dPsi8kgaK7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMgQno%2FbtsNfQi8VWC%2F1G0ekEEZdq8dPsi8kgaK7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GyUAe/btsNe4PpQW8/ks1uodMARvteJHfdWj3LAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GyUAe/btsNe4PpQW8/ks1uodMARvteJHfdWj3LAk/img.png&quot; data-origin-width=&quot;1153&quot; data-origin-height=&quot;755&quot; data-is-animation=&quot;false&quot; style=&quot;width: 58.2309%;&quot; data-widthpercent=&quot;58.92&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GyUAe/btsNe4PpQW8/ks1uodMARvteJHfdWj3LAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGyUAe%2FbtsNe4PpQW8%2Fks1uodMARvteJHfdWj3LAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1153&quot; height=&quot;755&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Custom Target &amp;amp; SubTarget 추가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UnityEditor.ShaderGraph.Target 클래스를 상속 받아 내용을 구현하면, 간단하게 ShaderGraph에 Target 추가 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 코드는 기존 패키지 스크립트를 복제해서 제작했기 때문에 설명 생략합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;1060&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lPHsH/btsNgfWv28L/hWGBb1KykiRrPxSFbbe5Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lPHsH/btsNgfWv28L/hWGBb1KykiRrPxSFbbe5Fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lPHsH/btsNgfWv28L/hWGBb1KykiRrPxSFbbe5Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlPHsH%2FbtsNgfWv28L%2FhWGBb1KykiRrPxSFbbe5Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1672&quot; height=&quot;1060&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;1060&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 SubTarget를 상속받는 클래스를 제작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Target은 RenderPipeline 개념이고 SubTarget이 Lighting Model이라고 이해합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1255&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dz4LuF/btsNgiMtbFz/MndXwnbJE26mEfGUtMHaDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dz4LuF/btsNgiMtbFz/MndXwnbJE26mEfGUtMHaDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dz4LuF/btsNgiMtbFz/MndXwnbJE26mEfGUtMHaDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdz4LuF%2FbtsNgiMtbFz%2FMndXwnbJE26mEfGUtMHaDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1255&quot; height=&quot;436&quot; data-origin-width=&quot;1255&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SubTarget은 ShaderGraph에서 Material 인터페이스에 추가됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sLMcr/btsNfCFsxeX/HjxXvslvPdW7wVwtOY7rAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sLMcr/btsNfCFsxeX/HjxXvslvPdW7wVwtOY7rAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sLMcr/btsNfCFsxeX/HjxXvslvPdW7wVwtOY7rAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsLMcr%2FbtsNfCFsxeX%2FHjxXvslvPdW7wVwtOY7rAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;219&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Shader Generator 로직 연동&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ShaderGraph는 제작된 노드 정보를 기반으로 Shader 코드 파일을 조립 생성하는 방식으로 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 추가한 커스텀 Target &amp;amp; SubTarget이 원하는 Shader를 생성하기 위해서 재료가되는 Shader 코드 개발 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 개발하기 위해 ShaderGraph 패키지의 BuiltIn Target을 기반으로 작업합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;274&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mV8AD/btsNfJw1doP/3RjYke7KCMkUy8BvXAAatK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mV8AD/btsNfJw1doP/3RjYke7KCMkUy8BvXAAatK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mV8AD/btsNfJw1doP/3RjYke7KCMkUy8BvXAAatK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmV8AD%2FbtsNfJw1doP%2F3RjYke7KCMkUy8BvXAAatK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;274&quot; height=&quot;305&quot; data-origin-width=&quot;274&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generator는 ShaderPass.template 파일을 기준으로 템플릿 기반 코드 생성과 Define 전처리기 셰이더 코드 조립합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조립되는 셰이더 코드는 복잡하고 오류 추적이 까다로우니, 간단한 Unlit부터 제작하면 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 URP ShaderLibrary를 이용하기보다는 직접 새로 제작하는 것을 권장합니다.&amp;nbsp;&lt;br /&gt;(URP hlsl Include하면 Define 중복 선언 오류 발생하는데 해결 못함)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwgD9O/btsNgCqYcf2/WU40XposIR86wOh4Wz3ETk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwgD9O/btsNgCqYcf2/WU40XposIR86wOh4Wz3ETk/img.png&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;351&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;59.93&quot; style=&quot;width: 59.2326%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwgD9O/btsNgCqYcf2/WU40XposIR86wOh4Wz3ETk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwgD9O%2FbtsNgCqYcf2%2FWU40XposIR86wOh4Wz3ETk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7hQHD/btsNfo0Ycfw/0a3k0ImZYlX2OySUcxUc6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7hQHD/btsNfo0Ycfw/0a3k0ImZYlX2OySUcxUc6K/img.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;565&quot; data-is-animation=&quot;false&quot; style=&quot;width: 39.6046%;&quot; data-widthpercent=&quot;40.07&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7hQHD/btsNfo0Ycfw/0a3k0ImZYlX2OySUcxUc6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7hQHD%2FbtsNfo0Ycfw%2F0a3k0ImZYlX2OySUcxUc6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;776&quot; height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZoBBo/btsNf7526IS/0PqrXbukZhQ5423jqRk8mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZoBBo/btsNf7526IS/0PqrXbukZhQ5423jqRk8mK/img.png&quot; data-origin-width=&quot;352&quot; data-origin-height=&quot;241&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;37.79&quot; style=&quot;width: 37.3498%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZoBBo/btsNf7526IS/0PqrXbukZhQ5423jqRk8mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZoBBo%2FbtsNf7526IS%2F0PqrXbukZhQ5423jqRk8mK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;352&quot; height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1uoJG/btsNhc6BG2c/RrKBqhDUhfMoNM1zUHrX1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1uoJG/btsNhc6BG2c/RrKBqhDUhfMoNM1zUHrX1k/img.png&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;356&quot; data-is-animation=&quot;false&quot; style=&quot;width: 61.4874%;&quot; data-widthpercent=&quot;62.21&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1uoJG/btsNhc6BG2c/RrKBqhDUhfMoNM1zUHrX1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1uoJG%2FbtsNhc6BG2c%2FRrKBqhDUhfMoNM1zUHrX1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;856&quot; height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위에서 제작한 Pass 구조체와 동일하게 SubShaders 구조체까지 제작하고 SubTarget 클래스 Setup 함수에서 추가되도록 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/107Hp/btsNg7jlyJ3/p5bEwLDCpsflATSm7lq1zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/107Hp/btsNg7jlyJ3/p5bEwLDCpsflATSm7lq1zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/107Hp/btsNg7jlyJ3/p5bEwLDCpsflATSm7lq1zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F107Hp%2FbtsNg7jlyJ3%2Fp5bEwLDCpsflATSm7lq1zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;898&quot; height=&quot;363&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BM3eL/btsNhb0TTw9/ZKYT3xrM2EzLKJ6POIPqaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BM3eL/btsNhb0TTw9/ZKYT3xrM2EzLKJ6POIPqaK/img.png&quot; data-alt=&quot;CustomRPUnlitSubTarget.cs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BM3eL/btsNhb0TTw9/ZKYT3xrM2EzLKJ6POIPqaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBM3eL%2FbtsNhb0TTw9%2FZKYT3xrM2EzLKJ6POIPqaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1467&quot; height=&quot;351&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CustomRPUnlitSubTarget.cs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 제작한 커스텀 SubTarget은 ShaderGraph Generator 클래스에서 코드 생성 호출됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Uya8J/btsNhpYINnr/NDtDRpbvoeK0cBNUlMVNn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Uya8J/btsNhpYINnr/NDtDRpbvoeK0cBNUlMVNn0/img.png&quot; data-alt=&quot;com.unity.shadergraph\Editor\Generation\Processors\Generator.cs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Uya8J/btsNhpYINnr/NDtDRpbvoeK0cBNUlMVNn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUya8J%2FbtsNhpYINnr%2FNDtDRpbvoeK0cBNUlMVNn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1221&quot; height=&quot;863&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;com.unity.shadergraph\Editor\Generation\Processors\Generator.cs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unlit Pass를 기반으로 CustomRP 추가 및 정상작동 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1861&quot; data-origin-height=&quot;853&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfaYpf/btsNhNygyJj/w6WGyLPdfCJgVVKQElDFn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfaYpf/btsNhNygyJj/w6WGyLPdfCJgVVKQElDFn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfaYpf/btsNhNygyJj/w6WGyLPdfCJgVVKQElDFn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfaYpf%2FbtsNhNygyJj%2Fw6WGyLPdfCJgVVKQElDFn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1861&quot; height=&quot;853&quot; data-origin-width=&quot;1861&quot; data-origin-height=&quot;853&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUPR28/btsNhcr5udg/53BEMkR0cKF2m9K9oMUrA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUPR28/btsNhcr5udg/53BEMkR0cKF2m9K9oMUrA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUPR28/btsNhcr5udg/53BEMkR0cKF2m9K9oMUrA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUPR28%2FbtsNhcr5udg%2F53BEMkR0cKF2m9K9oMUrA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;419&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;621&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BSADO/btsNfZNaByY/BLL08U7kr8P6GiarlcoNA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BSADO/btsNfZNaByY/BLL08U7kr8P6GiarlcoNA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BSADO/btsNfZNaByY/BLL08U7kr8P6GiarlcoNA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBSADO%2FbtsNfZNaByY%2FBLL08U7kr8P6GiarlcoNA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;226&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>custom lighting model</category>
      <category>shadergraph</category>
      <category>unity6</category>
      <category>유니티</category>
      <category>커스텀 라이팅 모델</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/186</guid>
      <comments>https://darkcatgame.tistory.com/186#entry186comment</comments>
      <pubDate>Thu, 10 Apr 2025 21:51:19 +0900</pubDate>
    </item>
    <item>
      <title>Unity Stencil Test 학습서</title>
      <link>https://darkcatgame.tistory.com/185</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Stencil Test란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stencil Buffer는 8bit 사이즈의 화면 버퍼이며 Fragment(픽셀)에 0 ~ 255 값을 기록합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stencil Test는 Stencil Buffer 정보를 기반으로 Fragment Shader를 동작시킬지 검사하는 작업이며 Rasterization 이후, Fragment Shader 전에 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stencil Test에 통과 못한 Fragment는 Discard(폐기)되어 Fragment Shader 호출되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Stencil Test에 통과되지 못하면 Fragment 연산 자체가 안된다는 뜻)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GPU 렌더링 파이프라인&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Vertex Stage&lt;/li&gt;
&lt;li&gt;Rasterization(Fragment 생성)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Stencil Test&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Depth(Z) Test&lt;/li&gt;
&lt;li&gt;Fragment Stage&lt;/li&gt;
&lt;li&gt;Blending &amp;amp; Color Write (프레임 버퍼 기록)&lt;/li&gt;
&lt;li&gt;Depth Write &amp;amp; &lt;b&gt;Stencil Write&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Unity URP HLSL 기반 Stencil Shader Sample&lt;/h4&gt;
&lt;pre id=&quot;code_1740138649644&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shader &quot;CatDarkGame/StencilSample&quot;
{
   Properties
    { 
        [MainTexture] _BaseMap(&quot;Texture&quot;, 2D) = &quot;white&quot; {}
        [MainColor] _BaseColor(&quot;Color&quot;, Color) = (1, 1, 1, 1)
        
        [Header(Cull Face)][Space(5)]
        [Enum(UnityEngine.Rendering.CullMode)] _CullMode(&quot;Cull Mode&quot;, Float) = 2
        
        [Header(Blend)][Space(5)]
        [Enum(UnityEngine.Rendering.BlendMode)] _SrcFactor(&quot;Src Factor&quot;, Float) = 1
        [Enum(UnityEngine.Rendering.BlendMode)] _DstFactor(&quot;Dst Factor&quot;, Float) = 0
        
        [Header(Depth)][Space(5)]
        [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest(&quot;Z Test&quot;, Float) = 4
        [Enum(Off, 0, On, 1)] _ZWrite(&quot;ZWrite&quot;, Float) = 1
        
        [Header(Stencil)][Space(5)]
        [IntRange] _StencilRef(&quot;Reference&quot;, Range(0,255)) = 0
        [IntRange] _StencilReadMask(&quot;Read Mask&quot;, Range(0,255)) = 255
        [IntRange] _StencilWriteMask(&quot;Write Mask&quot;, Range(0,255)) = 255
        [Space(5)]
        [Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp(&quot;Comparison&quot;, Int) = 8
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilPass(&quot;OP-Pass&quot;, Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilFail(&quot;OP-Fail&quot;, Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _ZFail(&quot;OP-ZFail&quot;, Int) = 0
        /*
        [Enum(UnityEngine.Rendering.CompareFunction)] _StencilCompBack(&quot;ComparisonBack&quot;, Int) = 8
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilPassBack(&quot;OP-PassBack&quot;, Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilFailBack(&quot;OP-FailBack&quot;, Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _ZFailBack(&quot;OP-ZFailBack&quot;, Int) = 0
        [Space(1)]
        [Enum(UnityEngine.Rendering.CompareFunction)] _StencilCompFront(&quot;ComparisonFront&quot;, Int) = 8
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilPassFront(&quot;OP-PassFront&quot;, Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilFailFront(&quot;OP-FailFront&quot;, Int) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _ZFailFront(&quot;OP-ZFailFront&quot;, Int) = 0
        
        */
    }

    SubShader
    {
         Tags
        {
            &quot;RenderType&quot; = &quot;Opaque&quot;
            &quot;RenderPipeline&quot; = &quot;UniversalPipeline&quot;
            &quot;UniversalMaterialType&quot; = &quot;Lit&quot;
            &quot;IgnoreProjector&quot; = &quot;True&quot;
        }

        Pass
        {
            Name  &quot;Forward&quot;
            Tags {&quot;LightMode&quot; = &quot;UniversalForward&quot;}

            Cull [_CullMode]
            ZWrite [_ZWrite]
            ZTest [_ZTest]
            Blend [_SrcFactor] [_DstFactor]
            
            Stencil
            {
                Ref [_StencilRef]
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]

                Comp [_StencilComp]
                Pass [_StencilPass]
                Fail [_StencilFail]
                ZFail [_ZFail]
                /*                             
                CompFront [_StencilCompBack]
                PassFront [_StencilPassBack]
                FailFront [_StencilFailBack]
                ZFailFront [_ZFailBack]

                CompBack [_StencilCompFront]
                PassBack [_StencilPassFront]
                FailBack [_StencilFailFront]
                ZFailBack [_ZFailFront]
                */
            }

            HLSLPROGRAM
            #pragma target 4.5
            #pragma exclude_renderers gles d3d9
            
            #pragma vertex vert
            #pragma fragment frag

            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;
            
            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                half4 _BaseColor;
            CBUFFER_END

            TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);
            
            struct Attributes
            {
                float4 positionOS    : POSITION;
                float2 uv            : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS    : SV_POSITION;
                float2 uv            : TEXCOORD0;
            }; 
            

            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;

                float4 positionOS = input.positionOS;
                float3 positionWS = TransformObjectToWorld(positionOS.xyz);
                float4 positionCS = TransformWorldToHClip(positionWS);

                output.positionCS = positionCS;
                output.uv = input.uv * _BaseMap_ST.xy + _BaseMap_ST.zw;
                return output;
            }

            half4 frag(Varyings input) : SV_Target
            {
                float2 baseMapUV = input.uv.xy;
                half4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, baseMapUV);
              
                half4 finalColor = col * _BaseColor;
                return finalColor;
            }
            
            ENDHLSL
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Stencil 커맨드 설명&lt;/h4&gt;
&lt;pre id=&quot;code_1740139257973&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Stencil {
    Ref 1         // Stencil 비교 기준값 (Reference Value)
    Comp Equal    // Stencil Buffer 값 == Ref(1)일 때 통과
    Pass Keep     // Stencil Test 통과 시, Stencil Buffer 값 변경 없음
    Fail Zero     // Stencil Test 실패 시, Stencil Buffer 값을 0으로 설정
}&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 139px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot; colspan=&quot;2&quot;&gt;&lt;b&gt; Stencil OP 옵션 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 27.3256%; height: 20px;&quot;&gt;Keep&lt;/td&gt;
&lt;td style=&quot;width: 72.6744%; height: 20px;&quot;&gt;기존 Stencil Buffer 값을 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 27.3256%; height: 17px;&quot;&gt;Zero&lt;/td&gt;
&lt;td style=&quot;width: 72.6744%; height: 17px;&quot;&gt;Stencil Buffer 값을 0으로 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 27.3256%; height: 17px;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 72.6744%; height: 17px;&quot;&gt;Ref 값으로 Stencil Buffer 값을 변&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 27.3256%; height: 17px;&quot;&gt;IncrSat&lt;/td&gt;
&lt;td style=&quot;width: 72.6744%; height: 17px;&quot;&gt;Stencil Buffer 값을 1 증가 (최대 255, 초과 시 255 유지)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 27.3256%; height: 17px;&quot;&gt;DecrSat&lt;/td&gt;
&lt;td style=&quot;width: 72.6744%; height: 17px;&quot;&gt;Stencil Buffer 값을 1 감소 (최소 0, 이하 시 0 유지)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 27.3256%; height: 17px;&quot;&gt;Invert&lt;/td&gt;
&lt;td style=&quot;width: 72.6744%; height: 17px;&quot;&gt;Stencil Buffer 비트를 반전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 27.3256%; height: 17px;&quot;&gt;IncrWrap&lt;/td&gt;
&lt;td style=&quot;width: 72.6744%; height: 17px;&quot;&gt;Stencil Buffer 값을 1 증가 (255 초과 시 0으로 Wrap)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 27.3256%; height: 17px;&quot;&gt;DecrWrap&lt;/td&gt;
&lt;td style=&quot;width: 72.6744%; height: 17px;&quot;&gt;Stencil Buffer 값을 1 감소 (0 이하 시 255로 Wrap)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stencil Test 응용 예제 1 - Ref &amp;amp; Comp&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;523&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyoxTJ/btsMtc0nY6W/idOJkwck5dqyjw3fqx26wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyoxTJ/btsMtc0nY6W/idOJkwck5dqyjw3fqx26wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyoxTJ/btsMtc0nY6W/idOJkwck5dqyjw3fqx26wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyoxTJ%2FbtsMtc0nY6W%2FidOJkwck5dqyjw3fqx26wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;415&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;523&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 102px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 26.5697%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Red&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.843%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Green&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.2151%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;Blue&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt; Render Queue &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.5697%; height: 17px; text-align: center;&quot;&gt;2000&lt;/td&gt;
&lt;td style=&quot;width: 25.843%; height: 17px; text-align: center;&quot;&gt;2001&lt;/td&gt;
&lt;td style=&quot;width: 24.2151%; text-align: center; height: 17px;&quot;&gt;2002&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;ZTest&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.5697%; height: 17px; text-align: center;&quot;&gt;LessEqual&lt;/td&gt;
&lt;td style=&quot;width: 25.843%; height: 17px; text-align: center;&quot;&gt;Disable&lt;/td&gt;
&lt;td style=&quot;width: 24.2151%; text-align: center; height: 17px;&quot;&gt;Disable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Stencil Ref&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.5697%; height: 17px; text-align: center;&quot;&gt;128&lt;/td&gt;
&lt;td style=&quot;width: 25.843%; height: 17px; text-align: center;&quot;&gt;128&lt;/td&gt;
&lt;td style=&quot;width: 24.2151%; text-align: center; height: 17px;&quot;&gt;128&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Stencil Comp&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.5697%; height: 17px; text-align: center;&quot;&gt;Always&lt;/td&gt;
&lt;td style=&quot;width: 25.843%; height: 17px; text-align: center;&quot;&gt;Equal&lt;/td&gt;
&lt;td style=&quot;width: 24.2151%; text-align: center; height: 17px;&quot;&gt;Equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;StencilOp Pass&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.5697%; height: 17px; text-align: center;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 25.843%; height: 17px; text-align: center;&quot;&gt;Zero&lt;/td&gt;
&lt;td style=&quot;width: 24.2151%; text-align: center; height: 17px;&quot;&gt;Keep&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해석&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stencil Test는 Pass 별로 동작하기 때문에 Render Queue(렌더 순서)에 영향 받습니다.&lt;/li&gt;
&lt;li&gt;Red의 StencilOp Pass는 Replace이기 때문에 Stencil Test에 통과된 픽셀에 Stencil Buffer에 Stencil Ref 값 128을 기록합니다.&lt;/li&gt;
&lt;li&gt;Red의 Stencil Comp가 Always이기 때문에 Stencil Test를 항상 통과합니다. 하지만 ZTest가 LessEqual이기 때문에 Red보다 앞에 그려진 픽셀은 렌더링되지 않습니다.&lt;/li&gt;
&lt;li&gt;Green과&amp;nbsp;Blue는&amp;nbsp;Red&amp;nbsp;뒤에&amp;nbsp;위치하기&amp;nbsp;때문에&amp;nbsp;ZTest를&amp;nbsp;비활성화해야&amp;nbsp;렌더링될&amp;nbsp;수&amp;nbsp;있습니다.&lt;/li&gt;
&lt;li&gt;Green은 Stencil Comp가 Equal이기 때문에 Green이 렌더링되는 픽셀의 Stencil Buffer가 Stencil Ref 128과 동일해야 Stencil Test 통과됩니다.&lt;/li&gt;
&lt;li&gt;Green의 StencilOp Pass가 Zero이기 때문에 Green의 Stencil Test가 통과된 픽셀의 Stencil Buffer 값은 0이됩니다.&lt;/li&gt;
&lt;li&gt;Blue는 Green과 Stencil Ref &amp;amp; Comp 값이 동일하지만 Green이 렌더링되면서 Stencil Buffer 값을 0으로 만들었기 때문에 Green이 렌더링되지 않은 픽셀에만 렌더링됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Stencil Test 응용 예제 2 - Read &amp;amp; Write Mask&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;qwezxdsadwqe2.gif&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cj7pa2/btsMsPdj8Ue/CRsL4zHRQo7ETUrRksdqO0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cj7pa2/btsMsPdj8Ue/CRsL4zHRQo7ETUrRksdqO0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cj7pa2/btsMsPdj8Ue/CRsL4zHRQo7ETUrRksdqO0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cj7pa2/btsMsPdj8Ue/CRsL4zHRQo7ETUrRksdqO0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;799&quot; height=&quot;376&quot; data-filename=&quot;qwezxdsadwqe2.gif&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 154px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Red&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Green&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;캐릭터&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Blue&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Render Queue&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;2000&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;2001&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;2002&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;3000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ZTest&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 18px; text-align: center;&quot;&gt;LessEqual&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 18px; text-align: center;&quot;&gt;LessEqual&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 18px;&quot;&gt;LessEqual&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 18px; text-align: center;&quot;&gt;Always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Stencil Ref&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;128&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;129&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;131&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;Stencil Read Mask&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; text-align: center; height: 17px;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; text-align: center; height: 17px;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; text-align: center; height: 17px;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;Stencil Write Mask&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; text-align: center; height: 17px;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; text-align: center; height: 17px;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; text-align: center; height: 17px;&quot;&gt;255&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Stencil Comp&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;Always&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;Always&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;Always&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;Equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;StencilOp Pass&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;Zero&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;Zero&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;Keep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;StnecilOp ZFail&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; text-align: center; height: 17px;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; text-align: center; height: 17px;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; text-align: center; height: 17px;&quot;&gt;Keep&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Stencil Read &amp;amp; Write Mask 개념&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stencil Comp(비교)는 Integer(정수) 기준으로 연산합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stencil Read &amp;amp; Write Mask를 Stencil Buffer &amp;amp; Ref 값을 8비트 이진수 Mask 기준으로 처리됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 값이 255이기 때문에 8비트 전부를 기록하지만, 만약 Mask 값을 128로 세팅한다면 0번 비트만 Stencil Buffer에 기록합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cacrig/btsMug15g7q/YuLiXBtFkJqrtJ12J7DNlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cacrig/btsMug15g7q/YuLiXBtFkJqrtJ12J7DNlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cacrig/btsMug15g7q/YuLiXBtFkJqrtJ12J7DNlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcacrig%2FbtsMug15g7q%2FYuLiXBtFkJqrtJ12J7DNlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;524&quot; height=&quot;156&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시로 캐릭터의 Ref 값은 131이지만 Stencil Write가 1이기 때문에 7번 비트만 Stencil Buffer에 기록하게 되어 Red와 캐릭터가 겹쳐서 렌더링된 픽셀의 Stencil Buffer 값은 131이 아닌 129가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Stencil Test 응용 예제 3 - Stencil Volume Mask&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daOjYH/btsMtufuj7B/xxAwbK9d8PBFCoPtKmwGL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daOjYH/btsMtufuj7B/xxAwbK9d8PBFCoPtKmwGL0/img.png&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;548&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.8511%; margin-right: 10px;&quot; data-widthpercent=&quot;50.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daOjYH/btsMtufuj7B/xxAwbK9d8PBFCoPtKmwGL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaOjYH%2FbtsMtufuj7B%2FxxAwbK9d8PBFCoPtKmwGL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPwTYP/btsMsNtqY5a/VS7YkZKsqj14idx8sN9dTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPwTYP/btsMsNtqY5a/VS7YkZKsqj14idx8sN9dTK/img.png&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;556&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.9861%;&quot; data-widthpercent=&quot;49.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPwTYP/btsMsNtqY5a/VS7YkZKsqj14idx8sN9dTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPwTYP%2FbtsMsNtqY5a%2FVS7YkZKsqj14idx8sN9dTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 171px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Yellow&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Gray&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Red&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Green&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Render Queue&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;2000&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;2001&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 17px; text-align: center;&quot;&gt;3000&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;3001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;Cull&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; text-align: center; height: 17px;&quot;&gt;Back&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; text-align: center; height: 17px;&quot;&gt;Back&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; text-align: center; height: 17px;&quot;&gt;Back&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; text-align: center; height: 17px;&quot;&gt;Front&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ZTest&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 18px; text-align: center;&quot;&gt;LessEqual&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 18px; text-align: center;&quot;&gt;LessEqual&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 18px; text-align: center;&quot;&gt;GreaterEqual&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 18px; text-align: center;&quot;&gt;GreaterEqual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Stencil Ref&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;224&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;128&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Stencil Read Mask&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 17px; text-align: center;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;129&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Stencil Write Mask&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;255&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;255&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Stencil Comp&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;Always&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;Always&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 17px; text-align: center;&quot;&gt;Always&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;Less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;StencilOp Pass&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 17px; text-align: center;&quot;&gt;Keep&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;Keep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.3721%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;StnecilOp ZFail&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.7092%; height: 17px; text-align: center;&quot;&gt;Keep&lt;/td&gt;
&lt;td style=&quot;width: 19.782%; height: 17px; text-align: center;&quot;&gt;Keep&lt;/td&gt;
&lt;td style=&quot;width: 16.061%; height: 17px; text-align: center;&quot;&gt;Replace&lt;/td&gt;
&lt;td style=&quot;width: 21.0756%; height: 17px; text-align: center;&quot;&gt;Keep&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brirPY/btsMrwFS1u5/gv8TZkjkCW6Pn7kQZI3sE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brirPY/btsMrwFS1u5/gv8TZkjkCW6Pn7kQZI3sE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brirPY/btsMrwFS1u5/gv8TZkjkCW6Pn7kQZI3sE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrirPY%2FbtsMrwFS1u5%2Fgv8TZkjkCW6Pn7kQZI3sE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;319&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stencil Volume Mask는 Stencil &amp;amp; Depth Test를 통해 Volume과 겹친 픽셀을 검출하는 기법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stencil Deferred Additional Light, Per Object Shadow 등의 기술에 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/Shader &amp;amp; VFX</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/185</guid>
      <comments>https://darkcatgame.tistory.com/185#entry185comment</comments>
      <pubDate>Fri, 21 Feb 2025 22:46:52 +0900</pubDate>
    </item>
    <item>
      <title>Unity GPU Indirect Drawing 구현 가이드 &amp;amp; ISM 시스템 개발 과정</title>
      <link>https://darkcatgame.tistory.com/184</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 엔진에서 GPU Driven Rendering 시스템 구현을 위한 구조화 버퍼 설계 활용, 셰이더 및 렌더러 구현에 대한 내용을 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주요 내용&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;드로우콜 원리 설명과 DrawInstanced 이해&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU에 렌더 호출을 하기 위해 커맨드버퍼에 담는 정보와 유니티 엔진의 드로우콜 최적화 시스템에서 이를 어떻게 최적화하고 있는지를 설명합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1807&quot; data-origin-height=&quot;1010&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH6i9k/btsLOYC68iy/4RIAklEyAnsRyqbAPendOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH6i9k/btsLOYC68iy/4RIAklEyAnsRyqbAPendOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH6i9k/btsLOYC68iy/4RIAklEyAnsRyqbAPendOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH6i9k%2FbtsLOYC68iy%2F4RIAklEyAnsRyqbAPendOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1807&quot; height=&quot;1010&quot; data-origin-width=&quot;1807&quot; data-origin-height=&quot;1010&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;InstanceDraw 시스템 구현 튜토리얼&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DrawMeshInstanced, DrawMeshInstancedProcedural, DrawMeshInstancedIndirect까지 유니티 엔진에서 제공하는 InstanceDraw API 사용 방법과 DrawInstanced를 위한 구조화 버퍼 설계 및 구현 방법을 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이해하기 쉽도록 만든 샘플 프로젝트 공유합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6vvv9/btsLPOFkITj/DiTmxGA9wAQW7GtDscLsp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6vvv9/btsLPOFkITj/DiTmxGA9wAQW7GtDscLsp1/img.png&quot; data-origin-width=&quot;1423&quot; data-origin-height=&quot;765&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.6306%; margin-right: 10px;&quot; data-widthpercent=&quot;50.21&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6vvv9/btsLPOFkITj/DiTmxGA9wAQW7GtDscLsp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6vvv9%2FbtsLPOFkITj%2FDiTmxGA9wAQW7GtDscLsp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1423&quot; height=&quot;765&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yb1V1/btsLPmCACHJ/y6kSLGHdjIFIy3SXIH7G80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yb1V1/btsLPmCACHJ/y6kSLGHdjIFIy3SXIH7G80/img.png&quot; data-origin-width=&quot;1409&quot; data-origin-height=&quot;764&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.2066%;&quot; data-widthpercent=&quot;49.79&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yb1V1/btsLPmCACHJ/y6kSLGHdjIFIy3SXIH7G80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyb1V1%2FbtsLPmCACHJ%2Fy6kSLGHdjIFIy3SXIH7G80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1409&quot; height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;GPU Driven Rendering 시스템 개발 및 프로젝트 활용 사례&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 프로젝트에 DrawInstanced 시스템 활용하기 위해 언리얼 엔진의 ISM(Instanced Static Mesh) 시스템과 유사한 GPU Driven Rendering 개발 과정을 소개합니다. 프로젝트에 시스템 도입을 위한 인터페이스 설계와 런타임 성능을 최대한 높이기 위해 어떤 것들이 고려되었는지 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MxLBz/btsLN343FAQ/jAETa8YUY7BaopKZODC310/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MxLBz/btsLN343FAQ/jAETa8YUY7BaopKZODC310/img.png&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;777&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.7237%; margin-right: 10px;&quot; data-widthpercent=&quot;50.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MxLBz/btsLN343FAQ/jAETa8YUY7BaopKZODC310/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMxLBz%2FbtsLN343FAQ%2FjAETa8YUY7BaopKZODC310%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1419&quot; height=&quot;777&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CSDfN/btsLP8XRVv0/8o5ZkpLU4mv2271npRAb9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CSDfN/btsLP8XRVv0/8o5ZkpLU4mv2271npRAb9K/img.png&quot; data-origin-width=&quot;1407&quot; data-origin-height=&quot;780&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.1135%;&quot; data-widthpercent=&quot;49.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CSDfN/btsLP8XRVv0/8o5ZkpLU4mv2271npRAb9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCSDfN%2FbtsLP8XRVv0%2F8o5ZkpLU4mv2271npRAb9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1407&quot; height=&quot;780&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그 외&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트에 도입한 ISM 시스템의 다음 발전 방향을 소개합니다. Cluster Mesh Render와 유사한 방식으로 버텍스 인덱스 버퍼로 여러 메쉬를 1 드로우콜로 처리하는 등, 실험적인 기술 구현 내용을 설명합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ohjqv/btsLP5UnLGn/PPNqHl4nws4UfNLKtJ1YTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ohjqv/btsLP5UnLGn/PPNqHl4nws4UfNLKtJ1YTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ohjqv/btsLP5UnLGn/PPNqHl4nws4UfNLKtJ1YTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fohjqv%2FbtsLP5UnLGn%2FPPNqHl4nws4UfNLKtJ1YTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1419&quot; height=&quot;790&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;구글 슬라이드 :&lt;/b&gt; &lt;a href=&quot;https://docs.google.com/presentation/d/1xxy7ouNLvLhFBdHa1YXj8iWzaMOTPqgT/edit#slide=id.p1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.google.com/presentation/d/1xxy7ouNLvLhFBdHa1YXj8iWzaMOTPqgT/edit#slide=id.p1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736939378054&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Instanced Static Mesh 시스템 개발 .pptx&quot; data-og-description=&quot;Instanced Static Mesh 시스템 개발 유니티 엔진 GPU Instancing Renderer 시스템 오지후 email : dhwlgn12@gmail.com blog : https://darkcatgame.tistory.com/&quot; data-og-host=&quot;docs.google.com&quot; data-og-source-url=&quot;https://docs.google.com/presentation/d/1xxy7ouNLvLhFBdHa1YXj8iWzaMOTPqgT/edit#slide=id.p1&quot; data-og-url=&quot;https://docs.google.com/presentation/d/1xxy7ouNLvLhFBdHa1YXj8iWzaMOTPqgT/edit?usp=embed_facebook&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/d2iRfg/hyX4xxnm2U/jaEO9KMei3oL3hvjStd4Rk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1xxy7ouNLvLhFBdHa1YXj8iWzaMOTPqgT/edit#slide=id.p1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.google.com/presentation/d/1xxy7ouNLvLhFBdHa1YXj8iWzaMOTPqgT/edit#slide=id.p1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/d2iRfg/hyX4xxnm2U/jaEO9KMei3oL3hvjStd4Rk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Instanced Static Mesh 시스템 개발 .pptx&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Instanced Static Mesh 시스템 개발 유니티 엔진 GPU Instancing Renderer 시스템 오지후 email : dhwlgn12@gmail.com blog : https://darkcatgame.tistory.com/&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GPU Instancing 샘플 프로젝트&lt;/b&gt; : &lt;a href=&quot;https://github.com/CatDarkGame/GPUInstancingSample&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/CatDarkGame/GPUInstancingSample&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737024653994&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - CatDarkGame/GPUInstancingSample&quot; data-og-description=&quot;Contribute to CatDarkGame/GPUInstancingSample development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/CatDarkGame/GPUInstancingSample&quot; data-og-url=&quot;https://github.com/CatDarkGame/GPUInstancingSample&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/EU9wn/hyX4zB5OCq/WNAUKZi8bMEGDCvS5n7GZK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c9luSG/hyX0x60V4U/Lxlqa4fqDQup6Wrq8opwE0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/GPUInstancingSample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/CatDarkGame/GPUInstancingSample&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/EU9wn/hyX4zB5OCq/WNAUKZi8bMEGDCvS5n7GZK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c9luSG/hyX0x60V4U/Lxlqa4fqDQup6Wrq8opwE0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - CatDarkGame/GPUInstancingSample&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to CatDarkGame/GPUInstancingSample development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>drawmeshindirect</category>
      <category>gpu driven renderer</category>
      <category>gpu driven rendering</category>
      <category>gpu instancing</category>
      <category>instanced static mesh</category>
      <category>structuredbuffer</category>
      <category>unity</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/184</guid>
      <comments>https://darkcatgame.tistory.com/184#entry184comment</comments>
      <pubDate>Wed, 15 Jan 2025 20:11:15 +0900</pubDate>
    </item>
    <item>
      <title>붕괴 스타레일 해상도 별 RenderScale 확인</title>
      <link>https://darkcatgame.tistory.com/183</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스타레일은 해상도 설정은 어플리케이션 해상도는 고정이며 RenderScale만 조정됩니다.&lt;/li&gt;
&lt;li&gt;RenderDoc을 통해 파악된 해상도 설정 별로 RenderScale 조정 값을 정리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어플리케이션 해상도&lt;/b&gt; : 2560x1440&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%; text-align: center;&quot;&gt;&lt;b&gt;매우 높음&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(1494x840)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(-41.67%)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.907%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2346&quot; data-origin-height=&quot;1265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dc4Xqb/btsLFMI622V/sq6aM4oA5y0f0fTA3VucK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dc4Xqb/btsLFMI622V/sq6aM4oA5y0f0fTA3VucK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dc4Xqb/btsLFMI622V/sq6aM4oA5y0f0fTA3VucK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdc4Xqb%2FbtsLFMI622V%2Fsq6aM4oA5y0f0fTA3VucK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2346&quot; height=&quot;1265&quot; data-origin-width=&quot;2346&quot; data-origin-height=&quot;1265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%; text-align: center;&quot;&gt;&lt;b&gt;높음&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(1494x840)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(-41.67%)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.907%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2328&quot; data-origin-height=&quot;1251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OHZ79/btsLFap7MoF/nTjVQfseruKiHIkdBdNLp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OHZ79/btsLFap7MoF/nTjVQfseruKiHIkdBdNLp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OHZ79/btsLFap7MoF/nTjVQfseruKiHIkdBdNLp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOHZ79%2FbtsLFap7MoF%2FnTjVQfseruKiHIkdBdNLp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2328&quot; height=&quot;1251&quot; data-origin-width=&quot;2328&quot; data-origin-height=&quot;1251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%; text-align: center;&quot;&gt;&lt;b&gt;중간&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(1280x720)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(-50.0%)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.907%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2336&quot; data-origin-height=&quot;1223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVYv9A/btsLG7rMMWm/pHKbhS2vGEABj5fi1KGYN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVYv9A/btsLG7rMMWm/pHKbhS2vGEABj5fi1KGYN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVYv9A/btsLG7rMMWm/pHKbhS2vGEABj5fi1KGYN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVYv9A%2FbtsLG7rMMWm%2FpHKbhS2vGEABj5fi1KGYN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2336&quot; height=&quot;1223&quot; data-origin-width=&quot;2336&quot; data-origin-height=&quot;1223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%; text-align: center;&quot;&gt;&lt;b&gt;낮음&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(1138x640)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(-55.56%)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.907%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2320&quot; data-origin-height=&quot;1258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOlzz0/btsLFdAqPSc/pgGO8HuJAK0J5ogQdP5gf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOlzz0/btsLFdAqPSc/pgGO8HuJAK0J5ogQdP5gf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOlzz0/btsLFdAqPSc/pgGO8HuJAK0J5ogQdP5gf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOlzz0%2FbtsLFdAqPSc%2FpgGO8HuJAK0J5ogQdP5gf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2320&quot; height=&quot;1258&quot; data-origin-width=&quot;2320&quot; data-origin-height=&quot;1258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.093%; text-align: center;&quot;&gt;&lt;b&gt;매우 낮음&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(960x540)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(-62.5%)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.907%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chWemm/btsLHxw1zC4/RxUzfxccJ5ZTIW3B8X1Id0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chWemm/btsLHxw1zC4/RxUzfxccJ5ZTIW3B8X1Id0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chWemm/btsLHxw1zC4/RxUzfxccJ5ZTIW3B8X1Id0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchWemm%2FbtsLHxw1zC4%2FRxUzfxccJ5ZTIW3B8X1Id0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2334&quot; height=&quot;1254&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Misc</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/183</guid>
      <comments>https://darkcatgame.tistory.com/183#entry183comment</comments>
      <pubDate>Tue, 7 Jan 2025 22:59:58 +0900</pubDate>
    </item>
    <item>
      <title>Unity EditorWindow SerializedProperty 활용 예제</title>
      <link>https://darkcatgame.tistory.com/182</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOoP9S/btsLlKLxHeQ/EgKcWoiFAYG7cUkxb45lz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOoP9S/btsLlKLxHeQ/EgKcWoiFAYG7cUkxb45lz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOoP9S/btsLlKLxHeQ/EgKcWoiFAYG7cUkxb45lz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOoP9S%2FbtsLlKLxHeQ%2FEgKcWoiFAYG7cUkxb45lz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;182&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;용도&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Editor 툴 개발 시, 별도의 EditorWindow를 띄워 툴 조작이 필요한 경우 SerializedProperty 객체를 참조하는 방식으로 구현.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;EditorWindow&lt;/h4&gt;
&lt;pre id=&quot;code_1734435014277&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEditor;
using UnityEngine;

public class CustomGradientEditorWindow : EditorWindow
{
    public static void Open(SerializedProperty property, Object target)
    {
        CustomGradientEditorWindow window = CreateInstance&amp;lt;CustomGradientEditorWindow&amp;gt;();
        window.titleContent = new GUIContent(&quot;CustomGradientEditorWindow&quot;);
        window.gradientProperty = property;
        window.targetObject = target;
        
        window.ShowUtility();
    }
    
    private SerializedProperty gradientProperty; 
    private Object targetObject; 

    private void OnGUI()
    {
        if (gradientProperty == null || targetObject == null) return;
        
        EditorGUI.BeginChangeCheck();
        Gradient gradient = gradientProperty.gradientValue;
        gradient = EditorGUILayout.GradientField(gradient);

        if (EditorGUI.EndChangeCheck())
        {
            Undo.RecordObject(targetObject, &quot;Edit Gradient&quot;);
            gradientProperty.gradientValue = gradient;
            
            gradientProperty.serializedObject.ApplyModifiedProperties();
            EditorUtility.SetDirty(targetObject);
        }
        
        if (Event.current.keyCode == KeyCode.Escape) 
        {
            Close();
        }
    }
    
    // 다른 Window 클릭 이벤트
    private void OnLostFocus()
    {
        // Close();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CustomEditor - Monobehaviour&lt;/h4&gt;
&lt;pre id=&quot;code_1734435002703&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(GradientObjectSample))]
public class GradientObject_Editor : Editor
{
    private SerializedProperty gradientProperty;
    private void OnEnable()
    {
        gradientProperty = serializedObject.FindProperty(&quot;gradient&quot;);
    }

    public override void OnInspectorGUI()
    {
        EditorGUILayout.PropertyField(gradientProperty);

        if (GUILayout.Button(&quot;Custom Editor&quot;))
        {
            CustomGradientEditorWindow.Open(gradientProperty, target);
        }
    }
}

public class GradientObjectSample : MonoBehaviour
{
    [SerializeField] private Gradient gradient;
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/Programming</category>
      <category>Unity Editor</category>
      <category>unity editorwindow</category>
      <category>유니티</category>
      <category>유니티 에디터 스크립트</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/182</guid>
      <comments>https://darkcatgame.tistory.com/182#entry182comment</comments>
      <pubDate>Tue, 17 Dec 2024 20:31:25 +0900</pubDate>
    </item>
    <item>
      <title>URP 2D Sprite FakeShadow for 2.5d</title>
      <link>https://darkcatgame.tistory.com/181</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/URP2DSpriteFakeShadow/blob/main/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/CatDarkGame/URP2DSpriteFakeShadow/blob/main/README.md&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734102434066&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;URP2DSpriteFakeShadow/README.md at main &amp;middot; CatDarkGame/URP2DSpriteFakeShadow&quot; data-og-description=&quot;URP 2D Sprite FakeShadow for 2.5d. Contribute to CatDarkGame/URP2DSpriteFakeShadow development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/CatDarkGame/URP2DSpriteFakeShadow/blob/main/README.md&quot; data-og-url=&quot;https://github.com/CatDarkGame/URP2DSpriteFakeShadow/blob/main/README.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pyNTC/hyXKlr5jAt/QcwZxY9CcjiGrKpFNcy94k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Jd6bw/hyXKvVPLtj/l4Vnhsi19I8S5socPKVbzK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/URP2DSpriteFakeShadow/blob/main/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/CatDarkGame/URP2DSpriteFakeShadow/blob/main/README.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pyNTC/hyXKlr5jAt/QcwZxY9CcjiGrKpFNcy94k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Jd6bw/hyXKvVPLtj/l4Vnhsi19I8S5socPKVbzK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;URP2DSpriteFakeShadow/README.md at main &amp;middot; CatDarkGame/URP2DSpriteFakeShadow&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;URP 2D Sprite FakeShadow for 2.5d. Contribute to CatDarkGame/URP2DSpriteFakeShadow development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;zxcsadwqqwe.gif&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mw6uG/btsLjp0HlM9/V4TlY0x6HE5VuNaH0J0SKK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mw6uG/btsLjp0HlM9/V4TlY0x6HE5VuNaH0J0SKK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mw6uG/btsLjp0HlM9/V4TlY0x6HE5VuNaH0J0SKK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Mw6uG/btsLjp0HlM9/V4TlY0x6HE5VuNaH0J0SKK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;919&quot; height=&quot;439&quot; data-filename=&quot;zxcsadwqqwe.gif&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;zxcsadwqqwe22.gif&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdNPx7/btsLjut38t3/c9e9Up9Qjvw9USFmX22Usk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdNPx7/btsLjut38t3/c9e9Up9Qjvw9USFmX22Usk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdNPx7/btsLjut38t3/c9e9Up9Qjvw9USFmX22Usk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bdNPx7/btsLjut38t3/c9e9Up9Qjvw9USFmX22Usk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;920&quot; height=&quot;505&quot; data-filename=&quot;zxcsadwqqwe22.gif&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;zxcsadwqqwe22333.gif&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0O80s/btsLjbuQAtG/5E0vcNsoNbPv6VKlLOF1jK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0O80s/btsLjbuQAtG/5E0vcNsoNbPv6VKlLOF1jK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0O80s/btsLjbuQAtG/5E0vcNsoNbPv6VKlLOF1jK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b0O80s/btsLjbuQAtG/5E0vcNsoNbPv6VKlLOF1jK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;921&quot; height=&quot;410&quot; data-filename=&quot;zxcsadwqqwe22333.gif&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>sprite shadow</category>
      <category>unity6 urp2d</category>
      <category>urp 2d sprite shadow</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/181</guid>
      <comments>https://darkcatgame.tistory.com/181#entry181comment</comments>
      <pubDate>Sat, 14 Dec 2024 00:08:22 +0900</pubDate>
    </item>
    <item>
      <title>Unity Screen Space Camera 테스트</title>
      <link>https://darkcatgame.tistory.com/180</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 이유&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UGUI Canvas를 Screen Space Overlay로 처리하면 안되는 경우, 별도 UI 카메라를 추가로 사용해야 합니다.&lt;/li&gt;
&lt;li&gt;UI 카메라는 Culling, 각종 Pass 초기화 등 UI에 불필요한 기능이 동작하여 CPU 성능 저하가 발생합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Screen Space Overlay를 사용할 수 없는 상황에 관련 성능 저하를 극복할 수 있는 방법을 테스트&lt;/b&gt;했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 방향&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Screen Space Camera에 별도 UI 카메라가 아닌 메인 카메라를 그대로 사용&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;Layer Mask를 활용해 UI가 렌더링되지 않도록 합니다.&lt;/li&gt;
&lt;li&gt;Render Objects RendererFeature를 활용해 UI가 AfterPostProcess 단계에서 렌더링되도록 합니다.&lt;/li&gt;
&lt;li&gt;Unity 2022.3.38f1 환경에서 테스트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 결과&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;샘플 프로젝트에서 UI 기능은 문제 없이 동작&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;UI 카메라 사용에 의한 불필요한 기능 동작은 발생하지 않습니다. 하지만 Camera Transform 변화로 인한 Cavna Rebuild 발생합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;별도 UI 카메라 사용에 비해서 이 테스트가 얼마나 성능 개선이 되는지 검증이 필요&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;FXAA와 같이 Final Bilt 단계에서 동작하는 렌더패스가 UI에 영향을 줍니다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AfterRendering 단계에서 화면 버퍼에 접근하는 것은 쉽게 되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 내용&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Screen Space - Camera에 메인 카메라 세팅합니다.&lt;/li&gt;
&lt;li&gt;Render Objects 렌더 피처 추가 후, 후처리 이벤트 이후에 UI 레이어가 렌더링되록합니다. 이때 Depth Test를 Always로 세팅합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqbuwg/btsK6wGOXTZ/beDARVo8r4np1ILinmkGB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqbuwg/btsK6wGOXTZ/beDARVo8r4np1ILinmkGB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqbuwg/btsK6wGOXTZ/beDARVo8r4np1ILinmkGB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqbuwg%2FbtsK6wGOXTZ%2FbeDARVo8r4np1ILinmkGB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;281&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btGWln/btsK7MaROWr/iHD8EMOuBQV0C2GKdlqUrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btGWln/btsK7MaROWr/iHD8EMOuBQV0C2GKdlqUrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btGWln/btsK7MaROWr/iHD8EMOuBQV0C2GKdlqUrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtGWln%2FbtsK7MaROWr%2FiHD8EMOuBQV0C2GKdlqUrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;387&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세팅이 완료되면 렌더링은 아래와 같이 동작합니다.&lt;/li&gt;
&lt;li&gt;AfterRenderingPostProcessing 단계에서 UI가 렌더링되기 때문에 UI는 후처리 효과 영향 받지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2116&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3wCGs/btsK8rxhlMS/MkbUDxKXeUDGO7RgmVl3V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3wCGs/btsK8rxhlMS/MkbUDxKXeUDGO7RgmVl3V0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3wCGs/btsK8rxhlMS/MkbUDxKXeUDGO7RgmVl3V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3wCGs%2FbtsK8rxhlMS%2FMkbUDxKXeUDGO7RgmVl3V0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2116&quot; height=&quot;1170&quot; data-origin-width=&quot;2116&quot; data-origin-height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ColorSpace, 블랜딩 오류 검사를 위해 Screen Space Overlay와 비교해봤으며 샘플 프로젝트에서 이슈 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2313&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YPWey/btsK66t770Y/xcFlj6VfYkkCecgzrb1jmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YPWey/btsK66t770Y/xcFlj6VfYkkCecgzrb1jmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YPWey/btsK66t770Y/xcFlj6VfYkkCecgzrb1jmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYPWey%2FbtsK66t770Y%2FxcFlj6VfYkkCecgzrb1jmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2313&quot; height=&quot;852&quot; data-origin-width=&quot;2313&quot; data-origin-height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2309&quot; data-origin-height=&quot;819&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diM00C/btsK7Jk4YHc/WRUmBj5MyPPFbZFWLOIyMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diM00C/btsK7Jk4YHc/WRUmBj5MyPPFbZFWLOIyMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diM00C/btsK7Jk4YHc/WRUmBj5MyPPFbZFWLOIyMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiM00C%2FbtsK7Jk4YHc%2FWRUmBj5MyPPFbZFWLOIyMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2309&quot; height=&quot;819&quot; data-origin-width=&quot;2309&quot; data-origin-height=&quot;819&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카메라 Transform 변화되면 Canvas Rebuild 관련 기능이 동작합니다.&lt;/li&gt;
&lt;li&gt;아래 테스트는 에디터에서 진행했으며 실제 타겟 디바이스에서 UI 카메라 사용과 비교해서 얼마나 성능 이득이 생기는지 검증 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2278&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b77tfS/btsK6rMck3u/Addmg9Kh5aQJ9julTqmhFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b77tfS/btsK6rMck3u/Addmg9Kh5aQJ9julTqmhFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b77tfS/btsK6rMck3u/Addmg9Kh5aQJ9julTqmhFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb77tfS%2FbtsK6rMck3u%2FAddmg9Kh5aQJ9julTqmhFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2278&quot; height=&quot;1032&quot; data-origin-width=&quot;2278&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;1060&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vE4jN/btsK8oN4mb1/z9vGnT0aueOb2qXufQXsk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vE4jN/btsK8oN4mb1/z9vGnT0aueOb2qXufQXsk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vE4jN/btsK8oN4mb1/z9vGnT0aueOb2qXufQXsk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvE4jN%2FbtsK8oN4mb1%2Fz9vGnT0aueOb2qXufQXsk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1672&quot; height=&quot;1060&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;1060&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>screen space camera</category>
      <category>screen space overlay</category>
      <category>UGUI</category>
      <category>Unity UI</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/180</guid>
      <comments>https://darkcatgame.tistory.com/180#entry180comment</comments>
      <pubDate>Wed, 4 Dec 2024 22:20:35 +0900</pubDate>
    </item>
    <item>
      <title>Unity Light Probe Group Volume(Multiple Light Probe Group)</title>
      <link>https://darkcatgame.tistory.com/178</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;123asdas.gif&quot; data-origin-width=&quot;1449&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bab0Ji/btsK1rqEnZv/jWWKOgCYECGafw6cvBzJok/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bab0Ji/btsK1rqEnZv/jWWKOgCYECGafw6cvBzJok/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bab0Ji/btsK1rqEnZv/jWWKOgCYECGafw6cvBzJok/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bab0Ji/btsK1rqEnZv/jWWKOgCYECGafw6cvBzJok/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1449&quot; height=&quot;720&quot; data-filename=&quot;123asdas.gif&quot; data-origin-width=&quot;1449&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8DKKU/btsK0Oz1QyM/mzDUc3fRQmz9HQ0x8pVUyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8DKKU/btsK0Oz1QyM/mzDUc3fRQmz9HQ0x8pVUyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8DKKU/btsK0Oz1QyM/mzDUc3fRQmz9HQ0x8pVUyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8DKKU%2FbtsK0Oz1QyM%2FmzDUc3fRQmz9HQ0x8pVUyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;302&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Custom Volume 시스템 제작, 씬 Lighting Data에서 추출한 SH 정보를 셰이더에 직접 전달 및 연산 처리하는 방법으로 시도했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 유니티는 여러 Light Probe Group을 설치해도 하나의 Light Probe 정보로 씬에 반영되어 오픈 필드에서 사용하는데 이슈가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 접근 방법으로 씬에 Light Probe를 Volume 단위로 베이킹하여 관리 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 현상&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://discussions.unity.com/t/prefab-light-probes/735323&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://discussions.unity.com/t/prefab-light-probes/735323&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UM9pL/btsK1G8XCF5/DCcizjhPsjhy5hvl5DuqmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UM9pL/btsK1G8XCF5/DCcizjhPsjhy5hvl5DuqmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UM9pL/btsK1G8XCF5/DCcizjhPsjhy5hvl5DuqmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUM9pL%2FbtsK1G8XCF5%2FDCcizjhPsjhy5hvl5DuqmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;470&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/178</guid>
      <comments>https://darkcatgame.tistory.com/178#entry178comment</comments>
      <pubDate>Thu, 28 Nov 2024 22:29:17 +0900</pubDate>
    </item>
    <item>
      <title>Unity Package Manager My Assets(구매 에셋) 목록 CSV 추출</title>
      <link>https://darkcatgame.tistory.com/177</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity Package Manger에 표시되는 My Assets (구매 에셋) 목록을 CSV 데이터로 일괄 추출하는 스크립트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 동작 검증을 위한 샘플 코드인점 참고 바랍니다. (사용성이 조잡하다는 의미)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMR4ta/btsJ5ez6npS/mcKu7jiC3dgEEeqHNfbb5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMR4ta/btsJ5ez6npS/mcKu7jiC3dgEEeqHNfbb5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMR4ta/btsJ5ez6npS/mcKu7jiC3dgEEeqHNfbb5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMR4ta%2FbtsJ5ez6npS%2FmcKu7jiC3dgEEeqHNfbb5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;745&quot; height=&quot;459&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용법&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 스크립트 제작&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래 코드를 복사해서 프로젝트 내부 Editor 스크립트를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1728916181763&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEditor;
using UnityEngine;
using System.Reflection;
using System.IO;
using System.Text;

namespace CatDarkGame.Editor
{
    public class PackageManagerHooker : EditorWindow
    {
        private static string k_OutputFileName = &quot;MyAssetsList.csv&quot;;
        
        [MenuItem(&quot;Tools/PackageManager Hooker/Export MyAssets Info to CSV&quot;)]
        public static void ExportAssetStoreProductInfoToCSV()
        {
            var packageManagerWindowType = typeof(UnityEditor.Editor).Assembly.GetType(&quot;UnityEditor.PackageManager.UI.PackageManagerWindow&quot;);

            if (packageManagerWindowType == null)
            {
                Debug.LogError(&quot;먼저 Package Manager를 실행 후, My Assets 리스트를 로드하고 이 기능을 실행하세요.&quot;);
                return;
            }
            
            var packageManagerWindow = GetFirstInstance(packageManagerWindowType);
            if (packageManagerWindow == null) return;
            var packageList = GetPackageList(packageManagerWindow);
            if (packageList == null) return;
            var mAssetStoreCache = GetFieldValue(packageList, &quot;m_AssetStoreCallQueue&quot;, &quot;m_AssetStoreCache&quot;);
            if (mAssetStoreCache == null) return;
            var productInfos = GetFieldValue(mAssetStoreCache, &quot;m_SerializedProductInfos&quot;) as object[];
            if (productInfos == null)
            {
                Debug.LogError(&quot;m_SerializedProductInfos is null.&quot;);
                return;
            }
            
            StringBuilder csvBuilder = new StringBuilder();
            csvBuilder.AppendLine(&quot;category,displayName,publisherName,publishedDate,assetStoreLinkUrl&quot;);
            foreach (var productInfo in productInfos)
            {
                var data = ExtractProductInfo(productInfo);
                csvBuilder.AppendLine(string.Join(&quot;,&quot;, data));
            }
            SaveCSV(csvBuilder.ToString());
        }

        private static object GetFirstInstance(System.Type type)
        {
            var instances = Resources.FindObjectsOfTypeAll(type);
            if (instances.Length == 0)
            {
                Debug.LogError(&quot;No PackageManagerWindow instance found.&quot;);
                return null;
            }
            return instances[0];
        }

        private static object GetPackageList(object window)
        {
            var mRoot = GetFieldValue(window, &quot;m_Root&quot;);
            if (mRoot == null) return null;

            var packageListProperty = mRoot.GetType().GetProperty(&quot;packageList&quot;, BindingFlags.NonPublic | BindingFlags.Instance);
            if (packageListProperty == null)
            {
                Debug.LogError(&quot;packageList property not found.&quot;);
                return null;
            }

            return packageListProperty.GetValue(mRoot);
        }

        private static object GetFieldValue(object instance, string fieldName, string nestedFieldName = null)
        {
            var field = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
            if (field == null)
            {
                Debug.LogError($&quot;{fieldName} field not found.&quot;);
                return null;
            }

            var value = field.GetValue(instance);
            if (value == null)
            {
                Debug.LogError($&quot;{fieldName} is null.&quot;);
                return null;
            }

            if (!string.IsNullOrEmpty(nestedFieldName))
            {
                var nestedField = value.GetType().GetField(nestedFieldName, BindingFlags.NonPublic | BindingFlags.Instance);
                if (nestedField == null)
                {
                    Debug.LogError($&quot;{nestedFieldName} field not found.&quot;);
                    return null;
                }
                value = nestedField.GetValue(value);
            }

            return value;
        }

        private static string[] ExtractProductInfo(object productInfo)
        {
            var productInfoType = productInfo.GetType();
            string category = (string)productInfoType.GetField(&quot;category&quot;).GetValue(productInfo);
            string displayName = (string)productInfoType.GetField(&quot;displayName&quot;).GetValue(productInfo);
            string publisherName = (string)productInfoType.GetField(&quot;publisherName&quot;).GetValue(productInfo);
            string publishedDate = (string)productInfoType.GetField(&quot;publishedDate&quot;).GetValue(productInfo);
            var assetStoreLink = productInfoType.GetField(&quot;assetStoreLink&quot;).GetValue(productInfo);
            
            string assetStoreLinkUrl = &quot;&quot;;
            if (assetStoreLink != null)
            {
                assetStoreLinkUrl = (string)assetStoreLink.GetType().GetField(&quot;url&quot;).GetValue(assetStoreLink);
            }

            return new string[] { category, displayName, publisherName, publishedDate, assetStoreLinkUrl };
        }
        
        private static void SaveCSV(string csvData)
        {
            string filePath = EditorUtility.SaveFilePanel(
                &quot;Save CSV File&quot;,               
                Application.dataPath,                           
                k_OutputFileName,    
                &quot;csv&quot;                        
            );
            
            if (!string.IsNullOrEmpty(filePath))
            {
                File.WriteAllText(filePath, csvData);
                Debug.Log($&quot;CSV export complete: {filePath}&quot;);
            
                // 파일을 탐색기에서 선택
                EditorUtility.RevealInFinder(filePath);
            }
            else
            {
                Debug.LogWarning(&quot;File save canceled.&quot;);
            }
            AssetDatabase.Refresh();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Package Manager My Assets 목록 로드&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Package Manger 실행 후, My Assets 목록을 전부 로드합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Load / All까지 완료되야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kXfgF/btsJ6iO8Amo/T3Ewfl1rOdKrXnCcf1Bln1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kXfgF/btsJ6iO8Amo/T3Ewfl1rOdKrXnCcf1Bln1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kXfgF/btsJ6iO8Amo/T3Ewfl1rOdKrXnCcf1Bln1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkXfgF%2FbtsJ6iO8Amo%2FT3Ewfl1rOdKrXnCcf1Bln1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1119&quot; height=&quot;582&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크롤을 끝까지 천천히해서 로딩 아이콘이 전부 완료되도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J8iWk/btsJ7d8vv7w/HpN2kB62DxmlQ4XBCHafKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J8iWk/btsJ7d8vv7w/HpN2kB62DxmlQ4XBCHafKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J8iWk/btsJ7d8vv7w/HpN2kB62DxmlQ4XBCHafKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ8iWk%2FbtsJ7d8vv7w%2FHpN2kB62DxmlQ4XBCHafKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;463&quot; height=&quot;187&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 아무 파일 선택해서 Reimport&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트에서 아무 파일 선택해서 Reimport 합니다.&lt;/li&gt;
&lt;li&gt;이 작업이 되야 PackageManger 리스트에서 정보 참조가 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. CSV 추출 툴 실행&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 3번 과정 완료 후, Package Manager를 계속 켜 놓습니다.&lt;/li&gt;
&lt;li&gt;그리고 Export MyAssets Info to CSV 기능을 실행하면 CSV 파일로 추출됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJtQTF/btsJ4serHHA/ODZdMxs2guGH9KYZKmo7v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJtQTF/btsJ4serHHA/ODZdMxs2guGH9KYZKmo7v1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJtQTF/btsJ4serHHA/ODZdMxs2guGH9KYZKmo7v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJtQTF%2FbtsJ4serHHA%2FODZdMxs2guGH9KYZKmo7v1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;106&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMR4ta/btsJ5ez6npS/mcKu7jiC3dgEEeqHNfbb5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMR4ta/btsJ5ez6npS/mcKu7jiC3dgEEeqHNfbb5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMR4ta/btsJ5ez6npS/mcKu7jiC3dgEEeqHNfbb5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMR4ta%2FbtsJ5ez6npS%2FmcKu7jiC3dgEEeqHNfbb5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;745&quot; height=&quot;459&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;기타&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSV 파일은 &quot;,&quot; 문자로 Colume을 구분하는데, 따로 예외처리 하지 않아서 CSV 파일 데이터가 이상하게 저장될 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/opEHX/btsJ6ZIljIN/JcozPkV6JKkNUdvT4ZvVVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/opEHX/btsJ6ZIljIN/JcozPkV6JKkNUdvT4ZvVVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/opEHX/btsJ6ZIljIN/JcozPkV6JKkNUdvT4ZvVVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FopEHX%2FbtsJ6ZIljIN%2FJcozPkV6JKkNUdvT4ZvVVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;729&quot; height=&quot;130&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외 다른 Colume 정보&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728916932441&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var productInfoType = productInfo.GetType();
                                                            
                                                            string id = (string)productInfoType.GetField(&quot;id&quot;).GetValue(productInfo);
                                                            string packageName = (string)productInfoType.GetField(&quot;packageName&quot;).GetValue(productInfo);
                                                            string description = (string)productInfoType.GetField(&quot;description&quot;).GetValue(productInfo);
                                                            string publisherName = (string)productInfoType.GetField(&quot;publisherName&quot;).GetValue(productInfo);
                                                            string category = (string)productInfoType.GetField(&quot;category&quot;).GetValue(productInfo);
                                                            string versionString = (string)productInfoType.GetField(&quot;versionString&quot;).GetValue(productInfo);
                                                            string versionId = (string)productInfoType.GetField(&quot;versionId&quot;).GetValue(productInfo);
                                                            string publishedDate = (string)productInfoType.GetField(&quot;publishedDate&quot;).GetValue(productInfo);
                                                            string displayName = (string)productInfoType.GetField(&quot;displayName&quot;).GetValue(productInfo);
                                                            string state = (string)productInfoType.GetField(&quot;state&quot;).GetValue(productInfo);
                                                            string publishNotes = (string)productInfoType.GetField(&quot;publishNotes&quot;).GetValue(productInfo);
                                                            string firstPublishedDate = (string)productInfoType.GetField(&quot;firstPublishedDate&quot;).GetValue(productInfo);
                                                            string publisherLink = (string)productInfoType.GetField(&quot;publisherLink&quot;).GetValue(productInfo);
                                                            
                                                            var assetStoreLinkField = productInfoType.GetField(&quot;assetStoreLink&quot;);
                                                            var assetStoreLink = assetStoreLinkField.GetValue(productInfo);
                                                            string assetStoreLinkName = &quot;&quot;;
                                                            string assetStoreLinkUrl = &quot;&quot;;

                                                            if (assetStoreLink != null)
                                                            {
                                                                var assetStoreLinkType = assetStoreLink.GetType();
                                                                assetStoreLinkName = (string)assetStoreLinkType.GetField(&quot;name&quot;).GetValue(assetStoreLink);
                                                                assetStoreLinkUrl = (string)assetStoreLinkType.GetField(&quot;url&quot;).GetValue(assetStoreLink);
                                                            }&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Unity/Programming</category>
      <category>my assets</category>
      <category>package manger</category>
      <category>package manger 일괄 다운로드</category>
      <category>unity</category>
      <category>에셋 목록</category>
      <category>유니티 구매 에셋 목록</category>
      <category>유니티 에셋 목록</category>
      <category>유니티 에셋 스토어 구매 목록</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/177</guid>
      <comments>https://darkcatgame.tistory.com/177#entry177comment</comments>
      <pubDate>Mon, 14 Oct 2024 23:35:58 +0900</pubDate>
    </item>
    <item>
      <title>Unity ShaderGraph Custom Function &amp;amp; Node 제작 가이드</title>
      <link>https://darkcatgame.tistory.com/176</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0NWzL/btsJSHQQzfD/QPNPhpsdbbU3k3zy3IHKcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0NWzL/btsJSHQQzfD/QPNPhpsdbbU3k3zy3IHKcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0NWzL/btsJSHQQzfD/QPNPhpsdbbU3k3zy3IHKcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0NWzL%2FbtsJSHQQzfD%2FQPNPhpsdbbU3k3zy3IHKcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;577&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity ShaderGraph에서 Custom Function &amp;amp; Custom Node 제작 방법을 설명합니다.&lt;/li&gt;
&lt;li&gt;Custom Function은 별도 hlsl 텍스트 파일을 참조하는 방식 기반으로 기본적인 네이밍 규칙 및 ShaderGraph와 연동 방법을 설명합니다.&lt;/li&gt;
&lt;li&gt;Custom Node은 내장 노드와 같이 별도 노드를 제작하는 방법을 설명합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;*Unity 2022.3.38f1 기준 정보입니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Custom Function HLSL 제작 및 연동&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ShaderGraph에서 Custom Function 노드 생성 후, hlsl 텍스트 파일을 참조 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Custom Function 노드에서 직접 셰이더 코드를 작성 할 수 있지만, 에디터에서 코드 작성하는 것이 편합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EMt2I/btsJSOoCfEW/x9A6tnyboKco6Zm4QeWGr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EMt2I/btsJSOoCfEW/x9A6tnyboKco6Zm4QeWGr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EMt2I/btsJSOoCfEW/x9A6tnyboKco6Zm4QeWGr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEMt2I%2FbtsJSOoCfEW%2Fx9A6tnyboKco6Zm4QeWGr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;394&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; hlsl 코드 기본 구조 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음, Custom Function에 사용되는 hlsl 코드 기본 구조입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ifndef 코드는 보통 중복 include 선언 방지용 디파인문입니다, 구지 안써도 상관 없지만 해당 hlsl 파일을 다른 파일에서 include 선언 대응용으로 작성합니다.&lt;/li&gt;
&lt;li&gt;함수 네이밍은 &quot;함수이름_half&quot; &amp;amp; &quot;함수이름_float&quot; 2가지를 작성합니다. 함수 네이밍은 자유롭게 작성해도 상관 없습니다, 함수 이름 뒤에 자료형은 반드시 아래 규칙과 동일하게 작성 필요합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UeXqb/btsJTeUUYDT/ZNytnP9T3cknwc13F9snxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UeXqb/btsJTeUUYDT/ZNytnP9T3cknwc13F9snxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UeXqb/btsJTeUUYDT/ZNytnP9T3cknwc13F9snxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUeXqb%2FbtsJTeUUYDT%2FZNytnP9T3cknwc13F9snxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;622&quot; height=&quot;311&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;311&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 자료형은 Custom Function Node의 Precision(정밀도) 메뉴와 연동되는 부분이며 Single로 세팅하는 경우 &quot;함수이름_float&quot; 함수가 호출되는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPFLw3/btsJSPOCs9i/MQWuVsf2cgtgF4g9PHoMlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPFLw3/btsJSPOCs9i/MQWuVsf2cgtgF4g9PHoMlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPFLw3/btsJSPOCs9i/MQWuVsf2cgtgF4g9PHoMlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPFLw3%2FbtsJSPOCs9i%2FMQWuVsf2cgtgF4g9PHoMlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;328&quot; height=&quot;149&quot; data-origin-width=&quot;328&quot; data-origin-height=&quot;149&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 2가지 자료형 함수를 전부 작성하기 때문에 아래와 같이 기능 구현 부분을 별도 함수로 분리하면 코드 관리 효율이 증가합니다.&lt;br /&gt;(구현 내용 자료형은 define문으로 관리 가능)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vAsDL/btsJTKlyJkC/mkhwkQoWWKP2JNDuROYqwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vAsDL/btsJTKlyJkC/mkhwkQoWWKP2JNDuROYqwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vAsDL/btsJTKlyJkC/mkhwkQoWWKP2JNDuROYqwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvAsDL%2FbtsJTKlyJkC%2FmkhwkQoWWKP2JNDuROYqwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;726&quot; height=&quot;405&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Custom Function 노드에 HLSL 연동하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음, Custom Function 노드에 hlsl 연동 과정입니다. 이 부분에서 규칙이 맞지 않으면 에러가 발생하니 참고합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Source 항목에 hlsl 파일을 참조합니다.&lt;/li&gt;
&lt;li&gt;Name 항목에 hlsl에 구현한 함수 이름을 작성합니다, 이때 _float &amp;amp; _half 자료형 글자를 제외하고 작성합니다.&lt;/li&gt;
&lt;li&gt;Inputs &amp;amp; Outputs 항목에 함수 인자 값과 동일한 자료형으로 세팅합니다.&amp;nbsp;&lt;br /&gt;이름은 자유롭게 작성해도 되지만 자료형은 완벽히 동일해야합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WfbuS/btsJTK6XqY4/vifPWfRM3UwkbsfhGDHzKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WfbuS/btsJTK6XqY4/vifPWfRM3UwkbsfhGDHzKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WfbuS/btsJTK6XqY4/vifPWfRM3UwkbsfhGDHzKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWfbuS%2FbtsJTK6XqY4%2FvifPWfRM3UwkbsfhGDHzKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;722&quot; height=&quot;318&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh79WG/btsJTKeMk4w/WjwmF3ZRrZAqTXYiAI7Y11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh79WG/btsJTKeMk4w/WjwmF3ZRrZAqTXYiAI7Y11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh79WG/btsJTKeMk4w/WjwmF3ZRrZAqTXYiAI7Y11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh79WG%2FbtsJTKeMk4w%2FWjwmF3ZRrZAqTXYiAI7Y11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;543&quot; height=&quot;115&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정까지 진행하면 Custom Function이 정상 동작됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXCnFP/btsJSMLgpp8/2L23Y5Zp1jycCEpU952pnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXCnFP/btsJSMLgpp8/2L23Y5Zp1jycCEpU952pnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXCnFP/btsJSMLgpp8/2L23Y5Zp1jycCEpU952pnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXCnFP%2FbtsJSMLgpp8%2F2L23Y5Zp1jycCEpU952pnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1014&quot; height=&quot;330&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xMLur/btsJUVlWGZP/ak6HozbsBK9kKjkCzzFJtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xMLur/btsJUVlWGZP/ak6HozbsBK9kKjkCzzFJtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xMLur/btsJUVlWGZP/ak6HozbsBK9kKjkCzzFJtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxMLur%2FbtsJUVlWGZP%2Fak6HozbsBK9kKjkCzzFJtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;457&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;텍스처 샘플링 Custom Function 제작&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음, ShaderGraph가 아닌 Custon Function에서 텍스처 샘플링하는 방법 설명합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1pWqN/btsJSMLgrkF/LTpwGn3U5kBs6kcD4klrk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1pWqN/btsJSMLgrkF/LTpwGn3U5kBs6kcD4klrk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1pWqN/btsJSMLgrkF/LTpwGn3U5kBs6kcD4klrk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1pWqN%2FbtsJSMLgrkF%2FLTpwGn3U5kBs6kcD4klrk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1054&quot; height=&quot;348&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 작성하던 hlsl파일에 다른 함수를 추가로 작성해서 사용 가능합니다, CustomFunction에서 Name 항목에 작성한 함수 이름을 호출하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hlsl 파일에서는 샘플링을 위한 다른 hlsl 파일을 include 및 샘플링 코드를 작성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXXc35/btsJT6Wcp9E/24UBosyfAwP5Hmf63kRQ90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXXc35/btsJT6Wcp9E/24UBosyfAwP5Hmf63kRQ90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXXc35/btsJT6Wcp9E/24UBosyfAwP5Hmf63kRQ90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXXc35%2FbtsJT6Wcp9E%2F24UBosyfAwP5Hmf63kRQ90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;703&quot; height=&quot;340&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 텍스처 파라미터는 Texture2D &amp;amp; TEXTURE2D 자료형을 사용하지만, 위 코드에서는 UnityTexture2D 자료형을 사용합니다. UnityTexture2D 자료형은 Custom Function 노드 파라미터 항목의 Texture2D와 연동되는 구조체 변수입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZmruC/btsJT5JHmo2/vLT0TjyS00Cx6eaDoiBhFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZmruC/btsJT5JHmo2/vLT0TjyS00Cx6eaDoiBhFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZmruC/btsJT5JHmo2/vLT0TjyS00Cx6eaDoiBhFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZmruC%2FbtsJT5JHmo2%2FvLT0TjyS00Cx6eaDoiBhFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;292&quot; height=&quot;353&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UnityTexture2D 구조체에는 Texture2D와 SamplerState 변수 등이 포함되어 있어, 텍스처의 샘플러 정보가 같이 전달됩니다. UnityTexture2D.tex &amp;amp; .samplerstate 코드 작성으로 사용 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;*변수 이름을 &quot;texture&quot; &amp;amp; &quot;sampler&quot; 문자를 사용하면 에러 발생합니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mHiVq/btsJTf7ql7C/1p65O8aZrs4rKVA84OAV10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mHiVq/btsJTf7ql7C/1p65O8aZrs4rKVA84OAV10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mHiVq/btsJTf7ql7C/1p65O8aZrs4rKVA84OAV10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmHiVq%2FbtsJTf7ql7C%2F1p65O8aZrs4rKVA84OAV10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;150&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vESyA/btsJTMjlwrb/PRptQyAj5wn6ziTL39ETy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vESyA/btsJTMjlwrb/PRptQyAj5wn6ziTL39ETy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vESyA/btsJTMjlwrb/PRptQyAj5wn6ziTL39ETy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvESyA%2FbtsJTMjlwrb%2FPRptQyAj5wn6ziTL39ETy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;619&quot; height=&quot;115&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 노드 연동 부분입니다, Custom Function 노드 부분은 앞선 내용과 동일하며, 그 외 텍스처 노드에서 UV 정보를 꺼내서 제작한 노드에 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 언급한 UV 정보는 Material GUI에 보이는 Tile &amp;amp; Offset 값입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1pWqN/btsJSMLgrkF/LTpwGn3U5kBs6kcD4klrk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1pWqN/btsJSMLgrkF/LTpwGn3U5kBs6kcD4klrk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1pWqN/btsJSMLgrkF/LTpwGn3U5kBs6kcD4klrk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1pWqN%2FbtsJSMLgrkF%2FLTpwGn3U5kBs6kcD4klrk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1054&quot; height=&quot;348&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;텍스처 샘플링 Custom Function 제작 - 별도 샘플러 사용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음, 앞선 내용과 동일하지만 텍스처 샘플링 정보가 아닌 별도 샘플링 정보를 사용하는 방법입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uM4Ki/btsJSQNDBhg/WhTiwK5zd8pgue0zl0Fat1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uM4Ki/btsJSQNDBhg/WhTiwK5zd8pgue0zl0Fat1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uM4Ki/btsJSQNDBhg/WhTiwK5zd8pgue0zl0Fat1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuM4Ki%2FbtsJSQNDBhg%2FWhTiwK5zd8pgue0zl0Fat1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1107&quot; height=&quot;466&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hlsl 코드는 UnityTexture2D가 아닌 TEXTURE2D &amp;amp; SAMPLER 자료형을 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7J7AB/btsJUlrRuCC/4r2c5mdPzYDsKExHW9SVjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7J7AB/btsJUlrRuCC/4r2c5mdPzYDsKExHW9SVjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7J7AB/btsJUlrRuCC/4r2c5mdPzYDsKExHW9SVjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7J7AB%2FbtsJUlrRuCC%2F4r2c5mdPzYDsKExHW9SVjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;877&quot; height=&quot;257&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Custom Function 노드에서는 Bare Texture2D &amp;amp; Bare Sampler State 자료형을 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4crJq/btsJT8sLYA9/a4OAYyXeKLV2XhJTxtT9FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4crJq/btsJT8sLYA9/a4OAYyXeKLV2XhJTxtT9FK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4crJq/btsJT8sLYA9/a4OAYyXeKLV2XhJTxtT9FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4crJq%2FbtsJT8sLYA9%2Fa4OAYyXeKLV2XhJTxtT9FK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;282&quot; height=&quot;372&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 아래와 같이 노드 연결하면 동작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvfB0O/btsJTqt8IMK/9QsCLedYyQClEVimjZ4FM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvfB0O/btsJTqt8IMK/9QsCLedYyQClEVimjZ4FM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvfB0O/btsJTqt8IMK/9QsCLedYyQClEVimjZ4FM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvfB0O%2FbtsJTqt8IMK%2F9QsCLedYyQClEVimjZ4FM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;820&quot; height=&quot;465&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;465&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Custom Node 제작&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Create Node에서 검색되며 활용 가능한 Custom Node 제작 방법 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Custom Function과 같이 노드 제작 후 별도 세팅 필요 없이 내장 노드와 같이 사용 가능 한 것이 장점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 복잡한 기능은 제작하기 어렵습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qPzjS/btsJT5XhdCC/ny0n77tzoZ5on0RFBQSIuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qPzjS/btsJT5XhdCC/ny0n77tzoZ5on0RFBQSIuk/img.png&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;316&quot; data-is-animation=&quot;false&quot; style=&quot;width: 63.2601%; margin-right: 10px;&quot; data-widthpercent=&quot;64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qPzjS/btsJT5XhdCC/ny0n77tzoZ5on0RFBQSIuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqPzjS%2FbtsJT5XhdCC%2Fny0n77tzoZ5on0RFBQSIuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9BvUp/btsJUnQJRPJ/55Syo5HpCiu9onwIkZbCaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9BvUp/btsJUnQJRPJ/55Syo5HpCiu9onwIkZbCaK/img.png&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;302&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.5771%;&quot; data-widthpercent=&quot;36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9BvUp/btsJUnQJRPJ/55Syo5HpCiu9onwIkZbCaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9BvUp%2FbtsJUnQJRPJ%2F55Syo5HpCiu9onwIkZbCaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;294&quot; height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Assembly Definition Reference 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Custom Node 제작에 필요한 API는 URP Internal로 구현되어 있어 기본적으로 함수 접근이 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 URP 패키지 내부 코드로 인식될 수 있는 Assembly Difinition Reference 기능을 활용합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로젝트 Assets 폴더 내부에 &quot;Editor&quot; 폴더 생성합니다. (이름 틀리면 안됩니다)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Project탭에서 아래 메뉴를 통해 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; Assembly Difinition Reference파일을 생성 후 Unity.ShaderGraph.Editor을 선택합니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Project/Create/Assembly Difinition Reference&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cS615s/btsJTADukW8/ECsX18epW1NhxXIDYe1i1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cS615s/btsJTADukW8/ECsX18epW1NhxXIDYe1i1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cS615s/btsJTADukW8/ECsX18epW1NhxXIDYe1i1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcS615s%2FbtsJTADukW8%2FECsX18epW1NhxXIDYe1i1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;447&quot; height=&quot;211&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;211&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스크립트 생성 &amp;amp; 기본 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;노드이름&quot;+&quot;Node 이름으로 C# 스크립트를 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;135&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpsn7B/btsJUPTGxk9/t8j6kR3ck7ykDkvL1J0Ld0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpsn7B/btsJUPTGxk9/t8j6kR3ck7ykDkvL1J0Ld0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpsn7B/btsJUPTGxk9/t8j6kR3ck7ykDkvL1J0Ld0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpsn7B%2FbtsJUPTGxk9%2Ft8j6kR3ck7ykDkvL1J0Ld0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;216&quot; height=&quot;135&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;135&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Grayscale 커스텀 노드 스크립트입니다. 코드 작성 방법은 매우 까다롭기 때문에 자세한 문법은 생략하고 중요 규칙만 설명합니다. 다른 코드 예제는 URP 패키지를 참고하세요.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스 이름 &amp;amp; Node 이름 문자열 규칙이 일치해야합니다.&lt;/li&gt;
&lt;li&gt;static string 클래스에서 노드에 표시되는 input &amp;amp; output을 설정합니다.&lt;/li&gt;
&lt;li&gt;static string 클래스 return 값은 셰이더 코드 문자열입니다. 즉 이 부분에 문자열로 셰이더 코드를 작성해야합니다.&lt;/li&gt;
&lt;li&gt;셰이더 문자열 코드를 ShaderGraph Core부분에서 파싱해서 코드 생성 방식으로 동작합니다.&lt;/li&gt;
&lt;li&gt;이곳에 작성하는 셰이더 코드는 문법이 조금 다릅니다. $precision은 float을 의미합니다, $precision3은 float3을 의미합니다.&lt;/li&gt;
&lt;li&gt;dot, mul과 같은 기초 연산 함수는 기본적으로 지원하지만, 그외 include가 필요하거나 커스텀 함수 작성은 매우 복잡합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1727878791545&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Reflection;
using UnityEngine;

namespace UnityEditor.ShaderGraph
{
    [Title(&quot;CustomNode&quot;, &quot;GrayscaleColor&quot;)]     // Create Node 계층
    class GrayscaleColorNode : CodeFunctionNode
    {
        public GrayscaleColorNode()
        {
            name = &quot;GrayscaleColor&quot;;        // Node 이름
        }

        protected override MethodInfo GetFunctionToConvert()
        {
            return GetType().GetMethod(&quot;GrayscaleColor&quot;, BindingFlags.Static | BindingFlags.NonPublic);
        }

        static string GrayscaleColor(
            [Slot(0, Binding.None)] ColorRGB color,
            [Slot(1, Binding.None)] out Vector1 Out)
        {
            return 
@&quot;
{
    $precision luma = dot(color, $precision3(0.2126729, 0.7151522, 0.0721750));
    Out = luma.x;
}
&quot;;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/Shader &amp;amp; VFX</category>
      <category>custom function</category>
      <category>custom node</category>
      <category>shadergraph</category>
      <category>shadergraph custom node</category>
      <category>unity</category>
      <category>unity6</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/176</guid>
      <comments>https://darkcatgame.tistory.com/176#entry176comment</comments>
      <pubDate>Wed, 2 Oct 2024 23:29:05 +0900</pubDate>
    </item>
    <item>
      <title>[Unity6] GPU Resident Drawer 간단 스펙 확인</title>
      <link>https://darkcatgame.tistory.com/175</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;*Unity 6000.0.20f1 기준으로 작성된 자료입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;GPU&amp;nbsp;Resident&amp;nbsp;Drawer&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity 6에 신규로 추가된 &lt;b&gt;DOTS Instancing 기반 드로우콜 병목 최적화 시스템&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Transform &amp;amp; Mesh 등, 흔히 드로우콜 속성의 렌더 API를 최적화&lt;/b&gt;하며, SRP Batcher와 병행해서 사용 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mesh Renderer에 BatchRendererGroup 시스템이 내장&lt;/b&gt;된 것으로 보이며 DOTS Instancing 외에 &lt;b&gt;GPU Culling 시스템까지 내장&lt;/b&gt;되어 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GPU Occlusion Culling은 Hierarchical-Z Occlusion Culling 시스템 사용하는 것으로 추측&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;li&gt;Additional Light, SH, Reflection Probe 및 LOD 등 &lt;b&gt;DOTS Instancing 셰이더 API가 이미 구현되어 있어 커스텀 셰이더 개발 시 활용 가능&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Forward+만 지원&lt;/b&gt;하며 위 DOTS Instancing 셰이더 API 또한 Forward+ 전용으로 구현되어 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;확인 &amp;amp; 예상되는 한계점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Forward+만 지원.&lt;/li&gt;
&lt;li&gt;Graphics API가 Vulkan 이상 디바이스만 지원. (Compute Shader &amp;amp; 구조화 버퍼 사용 가능해야함)&lt;/li&gt;
&lt;li&gt;Mesh Renderer만 지원.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;동일한 Mesh &amp;amp; Material의 오브젝트끼리 배칭 가능&lt;/b&gt;&lt;/u&gt;. 종류가 다양하면 오히려 성능 저하 예상.&lt;/li&gt;
&lt;li&gt;모바일 환경에서 GPU Culling 안정성 여부 검증 필요.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개인 평가&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 SRP Batcher만 사용했을때 많은 오브젝트에 의한 드로우콜 부하를 어느정도 완화 가능하겠지만 동일한 Mesh &amp;amp; Material끼리만 인스턴싱 가능하다는 부분에서 &lt;b&gt;반쪽짜리 인스턴싱 시스템으로 평가&lt;/b&gt;함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(Material 버퍼 인스턴싱은 추후 업데이트에서 될 가능성 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Mesh Index &amp;amp; Material Property 구조화 버퍼 구현을 통해 커스텀 가능하겠지만 GPU Culling 등 내장 시스템과 연동되지 않을 것이기 때문에 어차피 커스텀 Renderer 구현 필요함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DOTS 지원 셰이더 샘플&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1727094957935&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shader &quot;CatDarkGame/DOTSSample&quot;
{
    Properties
    { 
        [MainTexture] _BaseMap(&quot;Texture&quot;, 2D) = &quot;white&quot; {}
        [MainColor] _BaseColor(&quot;Color&quot;, Color) = (1, 1, 1, 1)
    }

    SubShader
    {
        Tags { &quot;RenderType&quot; = &quot;Opaque&quot; &quot;Queue&quot;=&quot;Geometry&quot; &quot;RenderPipeline&quot; = &quot;UniversalPipeline&quot; }
        LOD 100

        Pass
        {
            Name  &quot;DOTSSample&quot;
            Tags {&quot;LightMode&quot; = &quot;UniversalForward&quot;}

            HLSLPROGRAM
            #pragma target 4.5

            #pragma vertex vert
            #pragma fragment frag
            
            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;
            #include_with_pragmas &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl&quot;

            CBUFFER_START(UnityPerMaterial)
                float4  _BaseMap_ST;
                half4   _BaseColor;
            CBUFFER_END

            #ifdef UNITY_DOTS_INSTANCING_ENABLED
                UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
                    UNITY_DOTS_INSTANCED_PROP(float4, _BaseColor)   // float 자료형만 지원
                UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)

                // DOTS 전용 Static 변수에 값을 복제하여 사용하면 GPU 성능 높일 수 있다고함. (LitInput.hlsl 주석 참고)
                static float4 unity_DOTS_Sampled_BaseColor;
                void SetupDOTSLitMaterialPropertyCaches()  // UNITY_SETUP_INSTANCE_ID 디파인에서 호출됨
                {
                    unity_DOTS_Sampled_BaseColor            = UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float4, _BaseColor);
                }
                #undef UNITY_SETUP_DOTS_MATERIAL_PROPERTY_CACHES
                #define UNITY_SETUP_DOTS_MATERIAL_PROPERTY_CACHES() SetupDOTSLitMaterialPropertyCaches()
            
                #define _BaseColor              unity_DOTS_Sampled_BaseColor
            #endif
            
            TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);
            
            struct Attributes
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            }; 
            
            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;
                UNITY_SETUP_INSTANCE_ID(input);
                
                float4 positionOS = input.positionOS;
                float3 positionWS = TransformObjectToWorld(positionOS.xyz);
                float4 positionCS = TransformWorldToHClip(positionWS);

                output.positionCS = positionCS;
                output.uv = input.uv;
                return output;
            }

            float4 frag(Varyings input) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(input);
                float2 baseMapUV = input.uv.xy * _BaseMap_ST.xy + _BaseMap_ST.zw;
                float4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, baseMapUV);
                
                float4 finalColor = texColor * _BaseColor;
                return finalColor;
            }
            
            ENDHLSL
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity 도큐먼트 : &lt;a href=&quot;https://docs.unity3d.com/6000.0/Documentation/Manual/urp/gpu-resident-drawer.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.unity3d.com/6000.0/Documentation/Manual/urp/gpu-resident-drawer.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GPU ResidentDrawer 정리 자료 : &lt;a href=&quot;https://kj1241.github.io/graphics_development/UnityGDC24_GPUResidentDrawer&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kj1241.github.io/graphics_development/UnityGDC24_GPUResidentDrawer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity</category>
      <category>dots instancing</category>
      <category>gpu residentdrawer</category>
      <category>unity6</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/175</guid>
      <comments>https://darkcatgame.tistory.com/175#entry175comment</comments>
      <pubDate>Mon, 23 Sep 2024 21:50:06 +0900</pubDate>
    </item>
    <item>
      <title>URP 14 - SimplePostBlur</title>
      <link>https://darkcatgame.tistory.com/174</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1977&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkAwLl/btsIQqCeOsf/gBB81BzXAfSJ5m45QF8Qlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkAwLl/btsIQqCeOsf/gBB81BzXAfSJ5m45QF8Qlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkAwLl/btsIQqCeOsf/gBB81BzXAfSJ5m45QF8Qlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkAwLl%2FbtsIQqCeOsf%2FgBB81BzXAfSJ5m45QF8Qlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1977&quot; height=&quot;980&quot; data-origin-width=&quot;1977&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1259&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3PvGZ/btsISsekgMt/GNoS0RItiEhwlQ4uKJNPrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3PvGZ/btsISsekgMt/GNoS0RItiEhwlQ4uKJNPrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3PvGZ/btsISsekgMt/GNoS0RItiEhwlQ4uKJNPrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3PvGZ%2FbtsISsekgMt%2FGNoS0RItiEhwlQ4uKJNPrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1259&quot; height=&quot;621&quot; data-origin-width=&quot;1259&quot; data-origin-height=&quot;621&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모바일 성능을 고려한 블러 포스트프로세싱입니다.&lt;/li&gt;
&lt;li&gt;다운 샘플링 방식으로 블러를 표현하여 GPU 성능을 최대한 사용하지 않습니다.&lt;/li&gt;
&lt;li&gt;마스킹 텍스처를 활용해 블러 영역 제어 가능합니다.&lt;/li&gt;
&lt;li&gt;Unity 2022.3.38f1 / URP 14.0.3 버전 기준으로 제작되었습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URP 14의 최신 RendererFeature API를 적극 활용했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/SimplePostBlur/tree/main?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/CatDarkGame/SimplePostBlur/tree/main?tab=readme-ov-file&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722336844861&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - CatDarkGame/SimplePostBlur&quot; data-og-description=&quot;Contribute to CatDarkGame/SimplePostBlur development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/CatDarkGame/SimplePostBlur/tree/main?tab=readme-ov-file&quot; data-og-url=&quot;https://github.com/CatDarkGame/SimplePostBlur&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cyvdvD/hyWG0IYOp3/tB3FvEbddahCcktJkIkJD1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/SimplePostBlur/tree/main?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/CatDarkGame/SimplePostBlur/tree/main?tab=readme-ov-file&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cyvdvD/hyWG0IYOp3/tB3FvEbddahCcktJkIkJD1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - CatDarkGame/SimplePostBlur&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to CatDarkGame/SimplePostBlur development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;사용법&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. RendererFeature 추가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP Renderer Data에 PostBlurRendererFeature를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/elPCgL/btsIRZi8VJz/ISOCQ4Hy2SqduJRoOukQZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/elPCgL/btsIRZi8VJz/ISOCQ4Hy2SqduJRoOukQZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/elPCgL/btsIRZi8VJz/ISOCQ4Hy2SqduJRoOukQZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FelPCgL%2FbtsIRZi8VJz%2FISOCQ4Hy2SqduJRoOukQZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;131&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 51px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.496%; height: 17px;&quot;&gt;&lt;b&gt;Filter Mode&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.1706%; height: 17px;&quot;&gt;다운 샘플링 과정에서 RT 필터 모드를 선택합니다. &lt;br /&gt;성능 차이 거의 없으며, 원하는 비주얼에 맞게 세팅합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.496%; height: 17px;&quot;&gt;&lt;b&gt;Sample Count&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.1706%; height: 17px;&quot;&gt;다운 샘플 드로우콜 횟수를 의미합니다. &lt;br /&gt;숫자가 높을 수록 다운 샘플링 횟수가 많아지며 성능에 좋지 않습니다.&lt;br /&gt;런타임 도중 유동적으로 값을 바꾸는 것은 성능에 좋지 않습니다. &lt;br /&gt;숫자에 따라 비주얼이 느낌이 다르기 때문에 원하는 방향에 맞게 세팅합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.496%; height: 17px;&quot;&gt;&lt;b&gt;Render Pass Event&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.1706%; height: 17px;&quot;&gt;후처리 실행 구간을 세팅합니다.&lt;br /&gt;일반적으로 기본 값을 사용하지만 프로젝트 상황에 맞게 세팅합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Post Volume 추가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;씬에 Post Volume 배치 후, Post Blur Component를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I49Kq/btsISH3vZwe/icixj7RP8sRRbkWTlNQoS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I49Kq/btsISH3vZwe/icixj7RP8sRRbkWTlNQoS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I49Kq/btsISH3vZwe/icixj7RP8sRRbkWTlNQoS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI49Kq%2FbtsISH3vZwe%2Ficixj7RP8sRRbkWTlNQoS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;475&quot; height=&quot;164&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 51px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.8449%; height: 17px;&quot;&gt;&lt;b&gt;Blur Amount&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.8217%; height: 17px;&quot;&gt;Blur 효과 반영 수치를 제어합니다, 0으로 세팅하면 후처리 기능이 완전 비활성화됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.8449%; height: 17px;&quot;&gt;&lt;b&gt;Blur Offset&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.8217%; height: 17px;&quot;&gt;블러 퍼짐 수치를 제어합니다, 원하는 블러 비주얼 방향에 맞게 조절합니다.&lt;br /&gt;RendererFeature의 Sample Count와 다르게 런타임에서 유동적으로 값 제어해도 문제 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.8449%; height: 17px;&quot;&gt;&lt;b&gt;Blur Mask&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.8217%; height: 17px;&quot;&gt;마스크 텍스처를 통해 블러 영역을 제어 가능합니다.&lt;br /&gt;R채널만 사용하기 때문에 싱글 채널 무압축 포맷 사용을 권장합니다.&lt;br /&gt;마스크 텍스처 사이즈는 성능을 위해 최대한 작게 사용을 권장합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.8449%;&quot;&gt;&lt;b&gt;Blur Mask Tilling Offset&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 51.8217%;&quot;&gt;마스크 텍스처의 Tiling Offset 제어합니다. 응용 예시는 아래 설명합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 사용 예제 : TiltShift 세팅&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkAwLl/btsIQqCeOsf/gBB81BzXAfSJ5m45QF8Qlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkAwLl/btsIQqCeOsf/gBB81BzXAfSJ5m45QF8Qlk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;980&quot; data-origin-width=&quot;1977&quot; style=&quot;width: 77.0067%; margin-right: 10px;&quot; data-widthpercent=&quot;77.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkAwLl/btsIQqCeOsf/gBB81BzXAfSJ5m45QF8Qlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkAwLl%2FbtsIQqCeOsf%2FgBB81BzXAfSJ5m45QF8Qlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1977&quot; height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFMB1S/btsISGKfT7e/f5Axs2xUnQNJknDkqygOZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFMB1S/btsISGKfT7e/f5Axs2xUnQNJknDkqygOZ1/img.png&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;306&quot; data-is-animation=&quot;false&quot; style=&quot;width: 21.8305%;&quot; data-widthpercent=&quot;22.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFMB1S/btsISGKfT7e/f5Axs2xUnQNJknDkqygOZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFMB1S%2FbtsISGKfT7e%2Ff5Axs2xUnQNJknDkqygOZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;175&quot; height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TiltShift 블러를 세팅한 예제는 github 샘플 프로젝트에서 확인 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TiltShift는 화면 위 아래를 블러 처리하여 간단히 표현 가능하며, 주로 Fov가 낮은 미니어처 느낌을 낼때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용한 블러 마스크는 8x256 사이즈의 그라디언트 텍스처이며 Wrap Mode를 Mirror로 세팅하여 텍스처 사이즈 대비 더 높은 품질을 내도록 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;후처리에 사용하는 텍스처 사이즈 관련해서&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후처리 특성상 전체 해상도만큼 픽셀 쉐이더가 돌아가야하기 때문에 픽셀 연산 및 샘플링 비용에 민감할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Shared 메모리를 사용하는 모바일 디바이스 특성상 텍스처 샘플링 캐시 히트율이 낮으면 쉐이더 연산 상관 없이 성능 저하가 쉽게 발생하기 때문에 주의할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;구현 &amp;amp; 원리 설명&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 쉐이더 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7Qzz3/btsIQpiZ4IS/yv1AeUp89bp046UBv9Kikk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7Qzz3/btsIQpiZ4IS/yv1AeUp89bp046UBv9Kikk/img.png&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;463&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;25.58&quot; style=&quot;width: 25.2813%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7Qzz3/btsIQpiZ4IS/yv1AeUp89bp046UBv9Kikk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7Qzz3%2FbtsIQpiZ4IS%2Fyv1AeUp89bp046UBv9Kikk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;330&quot; height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIxhjS/btsIQxA9eDm/3KlEH1ganN3mvZtjJzA1dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIxhjS/btsIQxA9eDm/3KlEH1ganN3mvZtjJzA1dk/img.png&quot; data-origin-width=&quot;1097&quot; data-origin-height=&quot;529&quot; data-is-animation=&quot;false&quot; style=&quot;width: 73.5559%;&quot; data-widthpercent=&quot;74.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIxhjS/btsIQxA9eDm/3KlEH1ganN3mvZtjJzA1dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIxhjS%2FbtsIQxA9eDm%2F3KlEH1ganN3mvZtjJzA1dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1097&quot; height=&quot;529&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1개의 쉐이더의 각 역할이 다른 2개의 Pass가 구현되어 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pass 0 : 최종 블러 결과물 출력을 담당, 블러 처리된 RT와 Lerp 블랜딩 구현됨.&lt;/li&gt;
&lt;li&gt;Pass 1 : 다운 &amp;amp; 업 샘플링 Pass이며, 단순히 4방향 Box 블러가 구현됨.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. RenderPass - RenderTexture 세팅&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;361&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjq83v/btsIP40q35f/ncZG7W3kdtCz5S5bdHYvMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjq83v/btsIP40q35f/ncZG7W3kdtCz5S5bdHYvMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjq83v/btsIP40q35f/ncZG7W3kdtCz5S5bdHYvMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjq83v%2FbtsIP40q35f%2FncZG7W3kdtCz5S5bdHYvMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;361&quot; height=&quot;72&quot; data-origin-width=&quot;361&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V6oRj/btsISJ1hvRD/cOc4eO3LbAzUeLrNGT3Oq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V6oRj/btsISJ1hvRD/cOc4eO3LbAzUeLrNGT3Oq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V6oRj/btsISJ1hvRD/cOc4eO3LbAzUeLrNGT3Oq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV6oRj%2FbtsISJ1hvRD%2FcOc4eO3LbAzUeLrNGT3Oq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;284&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 화면 버퍼 카피(sourceTexture) &amp;amp; 출럭 버퍼(destTexture) RTHandle을 선언합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다운 샘플링 버퍼를 저장하는 RTHandle을 SampleCount 만큼 배열로 선언합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 14에서 한번 메모리에 생성한 버퍼를 재활용하기 때문에 고정 배열 형태로 선언하여 Sample Count가 변하지 않는 이상 추가 동적 할당이 발생하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1139&quot; data-origin-height=&quot;779&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p7OZg/btsIRd93Pa8/tRZpOK3AFHd7Euujzgw4q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p7OZg/btsIRd93Pa8/tRZpOK3AFHd7Euujzgw4q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p7OZg/btsIRd93Pa8/tRZpOK3AFHd7Euujzgw4q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp7OZg%2FbtsIRd93Pa8%2FtRZpOK3AFHd7Euujzgw4q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1139&quot; height=&quot;779&quot; data-origin-width=&quot;1139&quot; data-origin-height=&quot;779&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;445&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBP9iP/btsIReA98t9/wAKcRSkPKqe2r3eUSFCsI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBP9iP/btsIReA98t9/wAKcRSkPKqe2r3eUSFCsI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBP9iP/btsIReA98t9/wAKcRSkPKqe2r3eUSFCsI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBP9iP%2FbtsIReA98t9%2FwAKcRSkPKqe2r3eUSFCsI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;445&quot; height=&quot;113&quot; data-origin-width=&quot;445&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RT 생성하는 코드입니다. 다운 샘플링 -&amp;gt; 업 샘플링해야하기 때문에 &amp;gt;&amp;gt; 비트 시프트 연산자로 2배로 줄어들고 2배로 증가하도록 구현했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PnHLc/btsIR1OPMBw/ZRI5tzGsag4D8fKEwjcXV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PnHLc/btsIR1OPMBw/ZRI5tzGsag4D8fKEwjcXV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PnHLc/btsIR1OPMBw/ZRI5tzGsag4D8fKEwjcXV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPnHLc%2FbtsIR1OPMBw%2FZRI5tzGsag4D8fKEwjcXV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1119&quot; height=&quot;645&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 커맨드버퍼에 드로우콜 구현 내용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 14에서 권장하는 Triangles 메쉬로 화면 전체에 출력하는 방식으로 구현되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외 Material에 값을 직접 세팅하지 않고 MaterialPropertyBlock을 사용했는데, Blitter.BlitTexture 함수를 사용할 때 Material에 직접 값을 세팅해서 사용하면 세팅한 값이 초기화되지 않는 현상이 있어 이와 같이 구현했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 내장 코드에서도 이와 같이 구현된 것을 보면 MaterialPropertyBlock 사용이 권장 사항 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 결과 &amp;amp; 구현 특징 해설&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;34343.gif&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuweB2/btsISq1VgQI/TLZWS2srjcCokHNGMu3kuK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuweB2/btsISq1VgQI/TLZWS2srjcCokHNGMu3kuK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuweB2/btsISq1VgQI/TLZWS2srjcCokHNGMu3kuK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cuweB2/btsISq1VgQI/TLZWS2srjcCokHNGMu3kuK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;801&quot; height=&quot;359&quot; data-filename=&quot;34343.gif&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 설명한대로, 쉐이더에서는 박스 블러 외에 특별한 구현 내용이 없으며, 블러 효과는 다운 &amp;amp; 업 샘플링에 의존도가 높은 것이 특징입니다. 따라서 쉐이더 연산에 의한 GPU 성능보다는 RT 생성 &amp;amp; 드로우콜 처리에 의한 CPU 성능을 더 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 장점은 높은 해상도에 의한 성능 저하 부담이 적으며, RenderPass에서 프레임 스킵 구현을 통해 추가 최적화 작업이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/Shader &amp;amp; VFX</category>
      <category>Depth of Field</category>
      <category>DOF</category>
      <category>mobile blur</category>
      <category>tiltshift</category>
      <category>unity blur</category>
      <category>모바일 블러</category>
      <category>유니티 블러</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/174</guid>
      <comments>https://darkcatgame.tistory.com/174#entry174comment</comments>
      <pubDate>Tue, 30 Jul 2024 21:02:20 +0900</pubDate>
    </item>
    <item>
      <title>Unity Light Probe with out Lightmap</title>
      <link>https://darkcatgame.tistory.com/173</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;715&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coSBGt/btsIJEsyESx/cTCnhVYgggOneIjw2qEK90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coSBGt/btsIJEsyESx/cTCnhVYgggOneIjw2qEK90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coSBGt/btsIJEsyESx/cTCnhVYgggOneIjw2qEK90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoSBGt%2FbtsIJEsyESx%2FcTCnhVYgggOneIjw2qEK90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1452&quot; height=&quot;715&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;715&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Light Probe는 씬의 GI 정보를 베이킹하는 시스템입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서 LightMap 없이 Light Probe만 단독으로 사용 방법에 대한 리서칭 내용을 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;라이트 프로브 배치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 씬에 Light Probe를 배치합니다, Light Probe를 손으로 일일히 설치하는 방법도 있지만 Magic LightProbe 에셋을 활용하여 자동으로 배치 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Unity 2023에서부터 APV를 지원하지만 이 포스팅은 2023을 올리지 않는 프로젝트를 위해 작성했습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;a href=&quot;https://unity.com/kr/products/pricing-updates&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://unity.com/kr/products/pricing-updates&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1459&quot; data-origin-height=&quot;709&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uvXVb/btsIKcoOg1P/dnUNqzdfjR9O8EgJzu6KlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uvXVb/btsIKcoOg1P/dnUNqzdfjR9O8EgJzu6KlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uvXVb/btsIKcoOg1P/dnUNqzdfjR9O8EgJzu6KlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuvXVb%2FbtsIKcoOg1P%2FdnUNqzdfjR9O8EgJzu6KlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1459&quot; height=&quot;709&quot; data-origin-width=&quot;1459&quot; data-origin-height=&quot;709&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;라이트 베이킹&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Light Probe 베이킹을 동작 시키는 목적이기 때문에 베이킹 속도를 위해 Lightmap 세팅은 최저 옵션으로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lighting Mode는 Shadowmask로 세팅합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXs9pk/btsIHdwGHAb/P3364dE4ZLFgaLUdR9vkok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXs9pk/btsIHdwGHAb/P3364dE4ZLFgaLUdR9vkok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXs9pk/btsIHdwGHAb/P3364dE4ZLFgaLUdR9vkok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXs9pk%2FbtsIHdwGHAb%2FP3364dE4ZLFgaLUdR9vkok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;628&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플 씬에서 URP Lit 쉐이더를 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 라이트 베이킹하면 Static 오브젝트는 Lightmap에 구워진 GI 정보를 활용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic 오브젝트만 LightProbe의 GI 정보를 받게 되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;711&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzujmj/btsIHpDQMhq/LycTpETOkgRp4vBfFH0Op0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzujmj/btsIHpDQMhq/LycTpETOkgRp4vBfFH0Op0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzujmj/btsIHpDQMhq/LycTpETOkgRp4vBfFH0Op0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbzujmj%2FbtsIHpDQMhq%2FLycTpETOkgRp4vBfFH0Op0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1456&quot; height=&quot;711&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;711&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCkWQ9/btsIJ0oAbMt/XxAX2mE9EILiwkhMwcwxj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCkWQ9/btsIJ0oAbMt/XxAX2mE9EILiwkhMwcwxj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCkWQ9/btsIJ0oAbMt/XxAX2mE9EILiwkhMwcwxj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCkWQ9%2FbtsIJ0oAbMt%2FXxAX2mE9EILiwkhMwcwxj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;219&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Lit 쉐이더 수정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Static 오브젝트를 Lightmap 정보를 사용하지 않고 LightProbe GI 정보를 활용 할 수 있도록 쉐이더 수정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플 프로젝트에서는 URP Lit 쉐이더를 복제하여 수정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더 키워드 중에 LIGHTMAP_ON 키워드를 제거합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXDk3F/btsIHIwoaIe/ocw0zz3z5Uoglx6RvUkym0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXDk3F/btsIHIwoaIe/ocw0zz3z5Uoglx6RvUkym0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXDk3F/btsIHIwoaIe/ocw0zz3z5Uoglx6RvUkym0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXDk3F%2FbtsIHIwoaIe%2Focw0zz3z5Uoglx6RvUkym0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;172&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 아래와 같이 Lightmap의 GI 정보를 사용하지 않기 때문에 어둡게 나옵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCFW3P/btsIIYyhH3N/yQNU3H6zkcoTYIS0BIzsEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCFW3P/btsIIYyhH3N/yQNU3H6zkcoTYIS0BIzsEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCFW3P/btsIIYyhH3N/yQNU3H6zkcoTYIS0BIzsEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCFW3P%2FbtsIIYyhH3N%2FyQNU3H6zkcoTYIS0BIzsEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1430&quot; height=&quot;700&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 LitForwardPass의 InitializeInputData 함수에서 SAMPLE_GI 디파인 키워드 대신 SampleSH 함수를 직접 호출하게 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(예제에서는 기능 작동 확인을 위해 핵심 코드 수정만 설명드리니, 이 내용 이해를 기반으로 프로젝트에 알맞게 적용하시면 됩니다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJQa6F/btsIHVCe0tU/mIpkKcknu1bxILWYgSgSqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJQa6F/btsIHVCe0tU/mIpkKcknu1bxILWYgSgSqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJQa6F/btsIHVCe0tU/mIpkKcknu1bxILWYgSgSqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJQa6F%2FbtsIHVCe0tU%2FmIpkKcknu1bxILWYgSgSqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;797&quot; height=&quot;116&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Lightmap 베이킹한 배경 오브젝트를 복제하고 원본은 비활성화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 원본 오브젝트는 Lightmap 베이킹 모드로 전환되는지, 쉐이더 수정해도 반영되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 복제되어 새로 생성된 오브젝트는 Lightmap이 아닌 LightProbe GI 정보를 받게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2i0Zl/btsIHze74PO/ETON1Bay0VfBO7pFEUdLa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2i0Zl/btsIHze74PO/ETON1Bay0VfBO7pFEUdLa1/img.png&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;608&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;39.33&quot; style=&quot;width: 38.8761%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2i0Zl/btsIHze74PO/ETON1Bay0VfBO7pFEUdLa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2i0Zl%2FbtsIHze74PO%2FETON1Bay0VfBO7pFEUdLa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;584&quot; height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YQ4nb/btsIJEF6PHN/9O6NZ3LXkVrK0JEyCxcWP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YQ4nb/btsIJEF6PHN/9O6NZ3LXkVrK0JEyCxcWP0/img.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;405&quot; data-is-animation=&quot;false&quot; style=&quot;width: 59.9611%;&quot; data-widthpercent=&quot;60.67&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YQ4nb/btsIJEF6PHN/9O6NZ3LXkVrK0JEyCxcWP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYQ4nb%2FbtsIJEF6PHN%2F9O6NZ3LXkVrK0JEyCxcWP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : Lightmap 모드 우 : 일반 모드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE7nxx/btsIKiJj2Xd/FkpIiRsEvuxYMgnUP3Zv00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE7nxx/btsIKiJj2Xd/FkpIiRsEvuxYMgnUP3Zv00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE7nxx/btsIKiJj2Xd/FkpIiRsEvuxYMgnUP3Zv00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE7nxx%2FbtsIKiJj2Xd%2FFkpIiRsEvuxYMgnUP3Zv00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1428&quot; height=&quot;708&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12321312.gif&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JR8Vy/btsIKfFPGFI/uHefptmX0UDIP2rA60Muk0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JR8Vy/btsIKfFPGFI/uHefptmX0UDIP2rA60Muk0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JR8Vy/btsIKfFPGFI/uHefptmX0UDIP2rA60Muk0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/JR8Vy/btsIKfFPGFI/uHefptmX0UDIP2rA60Muk0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;363&quot; data-filename=&quot;12321312.gif&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 진행이 완료되면, 처음에 구웠던 라이트맵 텍스처는 더이상 사용할 일이 없으니 삭제해도 상관없습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zBUkb/btsIHpDQ2be/dwYwcu7JSHMXaXak9aIX60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zBUkb/btsIHpDQ2be/dwYwcu7JSHMXaXak9aIX60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zBUkb/btsIHpDQ2be/dwYwcu7JSHMXaXak9aIX60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzBUkb%2FbtsIHpDQ2be%2FdwYwcu7JSHMXaXak9aIX60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;220&quot; height=&quot;148&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;후처리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 SSAO와 Postprocess 효과를 적당히 입혀 씬 완성도를 높입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1455&quot; data-origin-height=&quot;709&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PvhK1/btsIKfZ80K1/ayWqO8a8rKKmVp02Y2mDXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PvhK1/btsIKfZ80K1/ayWqO8a8rKKmVp02Y2mDXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PvhK1/btsIKfZ80K1/ayWqO8a8rKKmVp02Y2mDXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPvhK1%2FbtsIKfZ80K1%2FayWqO8a8rKKmVp02Y2mDXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1455&quot; height=&quot;709&quot; data-origin-width=&quot;1455&quot; data-origin-height=&quot;709&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdehCA/btsIIiKLs3A/cox1uLd8QSigXYexIzuKdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdehCA/btsIIiKLs3A/cox1uLd8QSigXYexIzuKdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdehCA/btsIIiKLs3A/cox1uLd8QSigXYexIzuKdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdehCA%2FbtsIIiKLs3A%2Fcox1uLd8QSigXYexIzuKdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;693&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/173</guid>
      <comments>https://darkcatgame.tistory.com/173#entry173comment</comments>
      <pubDate>Mon, 22 Jul 2024 22:27:49 +0900</pubDate>
    </item>
    <item>
      <title>유니티 안드로이드 빌드 ADB Logcat 명령어</title>
      <link>https://darkcatgame.tistory.com/172</link>
      <description>&lt;h2 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;adb logcat 명령어를 통해 유니티 엔진 Console에서 확인 할 수 없는 로그 확인 가능합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;유니티 엔진에서 logcat 확인 가능한 에셋이 있지만, 이번 포스팅에서는 윈도우 cmd에서 확인하는 방법을 소개합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 자료&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블루스택으로 유니티 엔진 프로파일링하기 : &lt;a href=&quot;https://darkcatgame.tistory.com/131&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://darkcatgame.tistory.com/131&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ADB Logcat 명령어 가이드&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. ADB 연결&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1720007147412&quot; class=&quot;ebnf&quot; style=&quot;background-color: #f5f5f5; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;adb devices&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;*안드로이드 디바이스의 개발자 모드 및 USB 디버깅 활성화 필요.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 디바이스를 윈도우 PC에 연결 후, CMD에서 위 명령어를 입력합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 사진과 같이 연결된 디바이스 목록이 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brXzcx/btsIleoMscJ/6jvVNp3weoNR40YbdOu3bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brXzcx/btsIleoMscJ/6jvVNp3weoNR40YbdOu3bK/img.png&quot; data-alt=&quot;https://www.xda-developers.com/install-adb-windows-macos-linux/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brXzcx/btsIleoMscJ/6jvVNp3weoNR40YbdOu3bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrXzcx%2FbtsIleoMscJ%2F6jvVNp3weoNR40YbdOu3bK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;544&quot; height=&quot;209&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.xda-developers.com/install-adb-windows-macos-linux/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720007165162&quot; class=&quot;arduino&quot; style=&quot;background-color: #f5f5f5; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;adb connect 디바이스이름&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 connect 명령을 통해 디바이스 연결 진행합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이미 연결되어 있는 경우 에러 메세지가 출력되며 무시합니다. (ADB에 자동으로 연결되는 경우 이런 상황 발생)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Logcat 연결&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1720007211848&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;adb -s 디바이스이름 logcat -s Unity&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Logcat 로그 Clear&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1720007245395&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;adb logcat -c&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;디바이스 활성화되어 있는 동안 log가 계속 쌓이기 때문에 불편하면 Clear합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Misc</category>
      <category>LogCat</category>
      <category>유니티 프로파일링</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/172</guid>
      <comments>https://darkcatgame.tistory.com/172#entry172comment</comments>
      <pubDate>Wed, 3 Jul 2024 20:50:47 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 빌드 ADB 메모리 측정 (Release Build 메모리 측정)</title>
      <link>https://darkcatgame.tistory.com/171</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 엔진 게임 개발 과정에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;시스템 메모리 측정&lt;/b&gt;&lt;/span&gt;을 위해 메모리 프로파일러를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Development Build가 아닌 앱 메모리 측정이 필요한 경우, ADB 명령어를 활용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 윈도우 cmd에서 ADB 명령어를 활용해 메모리 측정 방법을 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주의&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메모리 측정 방식은 유니티 메모리 프로파일러의 결과와 차이가 발생 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 같은 앱이라도 디바이스 및 OS에 따라 ADB 측정 결과가 차이가 발생 할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ADB 메모리 측정 가이&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. ADB 연결&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720005125887&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;adb devices&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;*안드로이드 디바이스의 개발자 모드 및 USB 디버깅 활성화 필요.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드 디바이스를 윈도우 PC에 연결 후, CMD에서 위 명령어를 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진과 같이 연결된 디바이스 목록이 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bezMUD/btsIlDvecXt/fePyrBxcRDLrmJ7NrbnTR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bezMUD/btsIlDvecXt/fePyrBxcRDLrmJ7NrbnTR1/img.png&quot; data-alt=&quot;https://www.xda-developers.com/install-adb-windows-macos-linux/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bezMUD/btsIlDvecXt/fePyrBxcRDLrmJ7NrbnTR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbezMUD%2FbtsIlDvecXt%2FfePyrBxcRDLrmJ7NrbnTR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;549&quot; height=&quot;215&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.xda-developers.com/install-adb-windows-macos-linux/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720005506187&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;adb connect 디바이스이름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 connect 명령을 통해 디바이스 연결 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 연결되어 있는 경우 에러 메세지가 출력되며 무시합니다. (ADB에 자동으로 연결되는 경우 이런 상황 발생)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 앱 패키지 이름 확인&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1720005819732&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;adb shell pm list packages | grep 앱이름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 통해 디바이스에 설치된 앱 패키지 이름을 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwbt6m/btsIm2ACY1S/dzElo0YsCNxDPYO4AHLH3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwbt6m/btsIm2ACY1S/dzElo0YsCNxDPYO4AHLH3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwbt6m/btsIm2ACY1S/dzElo0YsCNxDPYO4AHLH3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwbt6m%2FbtsIm2ACY1S%2FdzElo0YsCNxDPYO4AHLH3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;570&quot; height=&quot;108&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 ADB 명령어 없이, 유니티 엔진 Project Settings/Other Settings의 Package Name을 그대로 사용해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 메모리 사용량 측정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1720006003438&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;adb shell dumpsys meminfo 패키지이름 -d&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 통해 특정 앱의 메모리 사용 정보 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;'-d'&lt;/b&gt;는 상세한 정보를 표시하는 필터링 명령어입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blogUv/btsImbEQjiQ/RndXsuFSHX7RFxBDxaptP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blogUv/btsImbEQjiQ/RndXsuFSHX7RFxBDxaptP0/img.png&quot; data-alt=&quot;메모리 용량 KB 단위 표시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blogUv/btsImbEQjiQ/RndXsuFSHX7RFxBDxaptP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblogUv%2FbtsImbEQjiQ%2FRndXsuFSHX7RFxBDxaptP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;775&quot; height=&quot;410&quot; data-origin-width=&quot;1111&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메모리 용량 KB 단위 표시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 정보가 표시되지만, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;전체 메모리는 Pss Total 메모리 용량을 확인&lt;/b&gt;&lt;/span&gt;하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 항목별 자세한 해석은 아래 Android Studio 도큐먼트 자료를 참고해주세요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.android.com/studio/profile/memory-profiler?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.android.com/studio/profile/memory-profiler?hl=ko&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Misc</category>
      <category>adb 메모리 측정</category>
      <category>메모리 프로파일러</category>
      <category>안드로이드</category>
      <category>유니티</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/171</guid>
      <comments>https://darkcatgame.tistory.com/171#entry171comment</comments>
      <pubDate>Wed, 3 Jul 2024 20:42:53 +0900</pubDate>
    </item>
    <item>
      <title>Unreal VertexAnimationTool.ms Unity 호환 버전</title>
      <link>https://darkcatgame.tistory.com/170</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언리얼 엔진 디렉토리 내부에 있는 VertexAnimationTool.ms는 3dsMax에서 VAT 리소스를 생성하는 스크립트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 리소스는 언리얼 엔진에서 바로 사용 가능하지만, 유니티 엔진에는 축(Axis)가 일치하지 않는 이유로 사용 할 수 없습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;VertexAnimationTool.ms 경로 :&lt;/b&gt; UE_5.3\Engine\Extras\3dsMaxScripts\VertexAnimationTool.ms&lt;/li&gt;
&lt;li&gt;&lt;b&gt;언리얼 VAT 튜토리얼 :&lt;/b&gt; &lt;a href=&quot;https://youtu.be/6Cain-u9_g4?feature=shared&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/6Cain-u9_g4?feature=shared&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅은 VertexAnimationTool.ms 스크립트를 유니티에서 사용할 수 있도록 수정하는 방법과 유니티 엔진 쉐이더 활용 예시를 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(맥스 스크립트 파일은 언리얼 설치 후 직접 획득합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;VertexAnimationTool.ms 수정 내용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;축 변경 외에 수정 사항 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 외에 중요한 부분은 &lt;u&gt;&lt;b&gt;3dsMax에서 Import &amp;amp; Export 과정에서 Unit 기준을 Meter로 세팅&lt;/b&gt;&lt;/u&gt;해야합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1717513677374&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 기존 코드
oldnormal=((((normalize (getTheVertexNormal currentMorphTarget j))*[1.0,-1.0,1.0])+1.0)*0.5)*255.0
append CurrentMorphTargetNormalArray oldnormal
originalVertPos=originalMeshVertPositions[j]
currentModelVertPos=getVertPos currentMorphTarget j
if (captureAbsolutePositions.checked) 
then (
	currentOffset=currentModelVertPos
)
else (
	currentOffset=(currentModelVertPos-originalVertPos)
)
currentOffset=[currentOffset[1],-1.0*currentOffset[2],currentOffset[3]]
currentOffset*=255.0
append currentMorphVertexOffsetArray currentOffset


-- 변경 코드
oldnormal=((((normalize (getTheVertexNormal currentMorphTarget j))*[1.0,1.0,-1.0])+1.0)*0.5)*255.0
append CurrentMorphTargetNormalArray oldnormal
originalVertPos=originalMeshVertPositions[j]
currentModelVertPos=getVertPos currentMorphTarget j
if (captureAbsolutePositions.checked) 
then (
	currentOffset=currentModelVertPos
)
else (
	currentOffset=(currentModelVertPos-originalVertPos)
)
currentOffset=[currentOffset[1], currentOffset[3], -1.0*currentOffset[2]]
currentOffset*=255.0
append currentMorphVertexOffsetArray currentOffset&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;유니티 쉐이더 사용 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플링 코드는 언리얼 엔진 MS_VertexAnimationTools_NorphTargets 노드의 핵심 로직을 코드로 변환했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1717514858220&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            float3 ConvertAxis(float3 v)
            {
               return v.xzy * float3(-1, -1, 1);
            }

			// Ref : UE MS_VertexAnimationTools_NorphTargets
            void GetVATData(float2 uv, out float3 positionOSOffset, out normalOS normalOSOffset)
            {
                float animFrameTotal = _AnimFrameTotal;
                float animFrameCurrent = lerp(0.0f, animTotalFrame, _AnimFrameAmount);

                float ceil_1 = 1.0f / ceil(animFrameTotal);
                float ceil_2 = ceil(animFrameCurrent);
                float mult_1 = ceil_1 * 0.5f;
                float mult_2 = ceil_2 * ceil_1;
                float add_1 = mult_1 + mult_2;
                float2 uv_VAT = float2(uv.x, 1.0f - add_1);
 
                float4 pos_Offset = SAMPLE_TEXTURE2D_LOD(_PositionVAT, sampler_PositionVAT, uv_VAT, 0);
                float4 nor_Offset = SAMPLE_TEXTURE2D_LOD(_NormalVAT, sampler_PositionVAT, uv_VAT, 0); 

                positionOSOffset = ConvertAxis(pos_Offset.xyz);
                normalOSOffset = ConvertAxis(nor_Offset.xyz);
            }
 
            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;
 
                float3 positionOSOffset = 0.0f;
                float3 normalOSOffset = 0.0f;
                GetVATData(input.uv_1, positionOSOffset, normalOSOffset);

                float3 positionOS = input.positionOS.xyz + positionOSOffset;
                float3 normalOS = normalOSOffset;
                output.positionCS = TransformObjectToHClip(positionOS);
                output.normalWS = TransformObjectToWorldNormal(normalOS);
                output.uv = input.uv_0;
                return output;
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>vertexanimationtool.ms</category>
      <category>축 국제 표준 안만들어?</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/170</guid>
      <comments>https://darkcatgame.tistory.com/170#entry170comment</comments>
      <pubDate>Wed, 5 Jun 2024 00:32:13 +0900</pubDate>
    </item>
    <item>
      <title>언리얼5 플레이어 카메라 변경 방법</title>
      <link>https://darkcatgame.tistory.com/169</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언리얼5.3.2 기준입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 엔진은 씬에 활성화된 카메라가 게임 뷰에 바로 반영되지만, 언리얼 엔진은 플레이어 액터를 직접 설정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 레벨(씬) 작업 과정에서 원하는 카메라로 플레이가 바로 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅은 가장 쉽게 원하는 카메라로 플레이가 되도록 하는 방법을 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨에 카메라 액터를 원하는 곳에 배치합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/93VnO/btsHdx3Q3eM/4szJGWHRFQh6kaDzOWRdo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/93VnO/btsHdx3Q3eM/4szJGWHRFQh6kaDzOWRdo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/93VnO/btsHdx3Q3eM/4szJGWHRFQh6kaDzOWRdo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F93VnO%2FbtsHdx3Q3eM%2F4szJGWHRFQh6kaDzOWRdo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;289&quot; height=&quot;549&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰 포트 상단의 아래 메뉴에서 레벨 블루 프린트를 Open합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨 블루 프린트는 해당 레벨의 스크립트 로직을 작성할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;레벨 블루 프린트 열기 단축키 : Ctrl + K&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FACpe/btsHg55L6Fq/qKrXM0qAmKNMQKfAGVvoB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FACpe/btsHg55L6Fq/qKrXM0qAmKNMQKfAGVvoB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FACpe/btsHg55L6Fq/qKrXM0qAmKNMQKfAGVvoB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFACpe%2FbtsHg55L6Fq%2FqKrXM0qAmKNMQKfAGVvoB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1144&quot; height=&quot;544&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진과 같이 블루 프린트를 구성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BeginPlay(게임 시작 이벤트)에서 바로 View Target을 설정하는 로직입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CameraActor는 레벨에 배치된 카메라를 드래그해서 가져오면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;623&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1MpQz/btsHdu0kr3z/vQAKys92nycnwyRhGQ5te0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1MpQz/btsHdu0kr3z/vQAKys92nycnwyRhGQ5te0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1MpQz/btsHdu0kr3z/vQAKys92nycnwyRhGQ5te0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1MpQz%2FbtsHdu0kr3z%2FvQAKys92nycnwyRhGQ5te0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1143&quot; height=&quot;623&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;623&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 블루 프린트 구성하기 어렵다면, 아래 텍스트를 복사해서 블루프린트에 붙여넣기하면 자동으로 세팅됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715089068484&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Begin Object Class=/Script/BlueprintGraph.K2Node_Event Name=&quot;K2Node_Event_0&quot; ExportPath=&quot;/Script/BlueprintGraph.K2Node_Event'/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap:PersistentLevel.ThirdPersonMap.EventGraph.K2Node_Event_0'&quot;
   EventReference=(MemberParent=&quot;/Script/CoreUObject.Class'/Script/Engine.Actor'&quot;,MemberName=&quot;ReceiveBeginPlay&quot;)
   bOverrideFunction=True
   NodePosX=192
   NodePosY=176
   NodeGuid=4E38FF194AB92D986D3EFF9DB43CE8FA
   CustomProperties Pin (PinId=894BA1854DFD7E17363BE688EA949010,PinName=&quot;OutputDelegate&quot;,Direction=&quot;EGPD_Output&quot;,PinType.PinCategory=&quot;delegate&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent=&quot;/Script/CoreUObject.Class'/Script/Engine.Actor'&quot;,MemberName=&quot;ReceiveBeginPlay&quot;),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=4F78317140C02B965FF125BD536BE799,PinName=&quot;then&quot;,Direction=&quot;EGPD_Output&quot;,PinType.PinCategory=&quot;exec&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_0 594F507347786C2E6C8EE88BB71F2EF3,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name=&quot;K2Node_CallFunction_0&quot; ExportPath=&quot;/Script/BlueprintGraph.K2Node_CallFunction'/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap:PersistentLevel.ThirdPersonMap.EventGraph.K2Node_CallFunction_0'&quot;
   FunctionReference=(MemberParent=&quot;/Script/CoreUObject.Class'/Script/Engine.PlayerController'&quot;,MemberName=&quot;SetViewTargetWithBlend&quot;)
   NodePosX=464
   NodePosY=160
   ErrorType=1
   ErrorMsg=&quot;이 블루프린트(셀프)는 PlayerController 이지 않으므로, \' Target \'에 연결이 있어야 합니다.&quot;
   NodeGuid=CA2D1DE440D3EF537866D09EA9948D8F
   CustomProperties Pin (PinId=594F507347786C2E6C8EE88BB71F2EF3,PinName=&quot;execute&quot;,PinToolTip=&quot;\n실행&quot;,PinType.PinCategory=&quot;exec&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_Event_0 4F78317140C02B965FF125BD536BE799,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=81A0E018461139864B0FC8ABE7748D78,PinName=&quot;then&quot;,PinToolTip=&quot;\n실행&quot;,Direction=&quot;EGPD_Output&quot;,PinType.PinCategory=&quot;exec&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=115E558D463323D44692C796315D3999,PinName=&quot;self&quot;,PinFriendlyName=NSLOCTEXT(&quot;K2Node&quot;, &quot;Target&quot;, &quot;Target&quot;),PinToolTip=&quot;Target\n플레이어 컨트롤러 오브젝트 레퍼런스&quot;,PinType.PinCategory=&quot;object&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=&quot;/Script/CoreUObject.Class'/Script/Engine.PlayerController'&quot;,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_3 E61E06F945C391EF864F59A206B93B8B,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=48FA77F242045A302EAE9CB25B74167B,PinName=&quot;NewViewTarget&quot;,PinToolTip=&quot;New View Target\n액터 오브젝트 레퍼런스\n\n새 뷰 타깃입니다.&quot;,PinType.PinCategory=&quot;object&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=&quot;/Script/CoreUObject.Class'/Script/Engine.Actor'&quot;,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_Literal_0 DFE458504126C450B71E1C952632FC77,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=CF5058AB4CABC3E46FAB5090ECD63EAB,PinName=&quot;BlendTime&quot;,PinToolTip=&quot;Blend Time\n플로트(단정밀도)\n\n블렌드에 걸릴 시간입니다.&quot;,PinType.PinCategory=&quot;real&quot;,PinType.PinSubCategory=&quot;float&quot;,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue=&quot;0.000000&quot;,AutogeneratedDefaultValue=&quot;0.000000&quot;,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=10682637496ACDA9100F9DB3E5DF6A11,PinName=&quot;BlendFunc&quot;,PinToolTip=&quot;Blend Func\nEViewTargetBlendFunction 열거형\n\n큐빅, 선형 혹은 기타 블렌딩 함수 설정입니다.&quot;,PinType.PinCategory=&quot;byte&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=&quot;/Script/CoreUObject.Enum'/Script/Engine.EViewTargetBlendFunction'&quot;,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue=&quot;VTBlend_Linear&quot;,AutogeneratedDefaultValue=&quot;VTBlend_Linear&quot;,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=38B9C70349D9A931262506A05355022D,PinName=&quot;BlendExp&quot;,PinToolTip=&quot;Blend Exp\n플로트(단정밀도)\n\nExponent, 블렌드 커브의 모양을 바꿀 수 있는 제곱근 값입니다.&quot;,PinType.PinCategory=&quot;real&quot;,PinType.PinSubCategory=&quot;float&quot;,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue=&quot;0.000000&quot;,AutogeneratedDefaultValue=&quot;0.000000&quot;,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=2774DE8D4B48AF8DC2B99183CAD5C472,PinName=&quot;bLockOutgoing&quot;,PinToolTip=&quot;Lock Outgoing\n부울\n\nTrue인 경우, 블렌드가 끝난 뒤에도 뷰 타깃을 마지막 프레임의  카메라 포지션으로 락 합니다.&quot;,PinType.PinCategory=&quot;bool&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue=&quot;false&quot;,AutogeneratedDefaultValue=&quot;false&quot;,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name=&quot;K2Node_CallFunction_3&quot; ExportPath=&quot;/Script/BlueprintGraph.K2Node_CallFunction'/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap:PersistentLevel.ThirdPersonMap.EventGraph.K2Node_CallFunction_3'&quot;
   bIsPureFunc=True
   FunctionReference=(MemberParent=&quot;/Script/CoreUObject.Class'/Script/Engine.GameplayStatics'&quot;,MemberName=&quot;GetPlayerController&quot;)
   NodePosX=96
   NodePosY=272
   NodeGuid=36EBC93A440DB759F37D30A1F1EAD4EF
   CustomProperties Pin (PinId=CB6D690843C19BBF83C3A3BE542F393B,PinName=&quot;self&quot;,PinFriendlyName=NSLOCTEXT(&quot;K2Node&quot;, &quot;Target&quot;, &quot;Target&quot;),PinToolTip=&quot;Target\n게임플레이 스태틱 오브젝트 레퍼런스&quot;,PinType.PinCategory=&quot;object&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=&quot;/Script/CoreUObject.Class'/Script/Engine.GameplayStatics'&quot;,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject=&quot;/Script/Engine.Default__GameplayStatics&quot;,PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=7BDA28BE475A7ACE9BEA78B7D4768CF8,PinName=&quot;WorldContextObject&quot;,PinToolTip=&quot;World Context Object\n오브젝트 레퍼런스&quot;,PinType.PinCategory=&quot;object&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=&quot;/Script/CoreUObject.Class'/Script/CoreUObject.Object'&quot;,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=True,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=AF460BFA4460B1B9C8B713B290433756,PinName=&quot;PlayerIndex&quot;,PinToolTip=&quot;Player Index\n인티저\n\n플레이어 컨트롤러 목록의 인덱스로, 로컬 플레이어로부터 시작하여 사용할 수 있는 리모트 플레이어로 가는 식입니다.&quot;,PinType.PinCategory=&quot;int&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue=&quot;0&quot;,AutogeneratedDefaultValue=&quot;0&quot;,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=E61E06F945C391EF864F59A206B93B8B,PinName=&quot;ReturnValue&quot;,PinToolTip=&quot;Return Value\n플레이어 컨트롤러 오브젝트 레퍼런스&quot;,Direction=&quot;EGPD_Output&quot;,PinType.PinCategory=&quot;object&quot;,PinType.PinSubCategory=&quot;&quot;,PinType.PinSubCategoryObject=&quot;/Script/CoreUObject.Class'/Script/Engine.PlayerController'&quot;,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_0 115E558D463323D44692C796315D3999,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unreal</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/169</guid>
      <comments>https://darkcatgame.tistory.com/169#entry169comment</comments>
      <pubDate>Tue, 7 May 2024 22:38:09 +0900</pubDate>
    </item>
    <item>
      <title>언리얼5 기본 단축키 정리 (유니티 사용자 추천)</title>
      <link>https://darkcatgame.tistory.com/168</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언리얼5.3.2 기준입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기본&amp;nbsp;단축키&amp;nbsp;정리&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;굵은 글자 = 자주 사용하는 기능&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;오브젝트 &amp;amp; 카메라&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 328px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Gizmo - Translate(이동) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; W &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Gizmo - Rotate(회전) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; E &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Gizmo - Scale(크기) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; R &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Grid Translate(이동) Snap Size 조절&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&amp;lsquo;[&amp;rsquo; (작게) / &amp;lsquo;]&amp;rsquo; (크게)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Grid Rotate(회전) Snap Size 조절&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Shift + &amp;lsquo;[&amp;rsquo; (작게) / &amp;lsquo;]&amp;rsquo; (크게)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Grid Snap Toggle &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; 없음 (직접 커스텀) &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 선택 오브젝트 Hide &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; H &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; UnHide All &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Ctrl + H &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 선택 오브젝트 카메라 포커스 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; F &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 선택 오브젝트 복사 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Ctrl + D / Alt + Gizmo 이동 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;선택 오브젝트 에셋 하이라이트&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Ctrl + B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Snap to Floor&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;End&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Snap Pivot to Floor&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Alt + End&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;카메라 Zoom In / Out&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;RMB + C(확대) / Z(축소)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 카메라 이동 속도 조절 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; RMB + Scroll &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Directional Light 방향 조절&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Ctrl + L &amp;rarr; Ctrl 유지 + 마우스 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 오브젝트를 뷰에 스냅 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 없음 (직접 커스텀) &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;뷰포트&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 348px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 와이어 프레임 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Alt + 2 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 언릿 (UnLit) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Alt + 3 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 라이팅 포함 (Lit) (기본) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Alt + 4 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 디테일 라이팅 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Alt + 5 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 라이팅만 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Alt + 6 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; 그리드 &amp;amp; 뷰포트 아이콘 보기 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; G &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Perspective View (3D) (기본) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Alt + G &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Orthographic View (2D) - Front&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Alt + H&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Orthographic View (2D) - Top&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Alt + J&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Orthographic View (2D) - Left&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Alt + K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Orthographic View (2D) - Right&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Alt + Shift + K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;에디터 모드 - 액터 배치 (기본)&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Shift + 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;에디터 모드 - 랜드 스케이프&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Shift + 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;에디터 모드 - 폴리지&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Shift + 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;에디터 모드 - 메시 페인트&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Shift + 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;에디터 모드 - 모델링&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Shift + 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;에디터 모드 - 프랙처&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Shift + 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;에디터 모드 - 브러시 편집&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Shift + 7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;에디터&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 250px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Play &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Alt + P &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Pause &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt;?&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Eject (Play 유지 &amp;amp; 자유 카메라) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; F8 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;에디터 Full Screen&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Shift + F11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;뷰포트 크게 보기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;F10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;뷰포트 Full Screen&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;F11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Save (레벨 저장) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Ctrl + S &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Save All (레벨 &amp;amp; 에셋 저장 / 컴파일) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Ctrl + Shift + S &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; 레벨 블루프린스/스크립트 열기 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Ctrl + K &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;에셋 탐색기 열기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Ctrl + P&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;선택 오브젝트 에셋 에디터 열기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Ctrl + E&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;선택 오브젝트 레퍼런스 뷰어 열기&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;Alt + Shift + R&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Undo &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;&lt;b&gt; Ctrl + Z &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Redo (다시 실행) (Undo 반대) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;b&gt; Ctrl + Y &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;단축키 커스텀&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 익숙하지 않거나, 단축키 할당 되지 않는 키를 커스텀하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단 바 &lt;b&gt;'편집' -&amp;gt; '에디터 개인설정'&lt;/b&gt; 창을 실행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cceFXO/btsHd8o6fgI/sUtKLxU5oA5eKcf3Qkz070/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cceFXO/btsHd8o6fgI/sUtKLxU5oA5eKcf3Qkz070/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cceFXO/btsHd8o6fgI/sUtKLxU5oA5eKcf3Qkz070/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcceFXO%2FbtsHd8o6fgI%2FsUtKLxU5oA5eKcf3Qkz070%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;381&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 그리드 스냅 Toggle&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리드 스냅 기능 On/Off 기능은 단축키가 없어 뷰포트에서 아래 표시한 버튼을 클릭해야합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;343&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDq0dO/btsHdu6585x/SKN9NFlBKXsMmKgbdv1OAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDq0dO/btsHdu6585x/SKN9NFlBKXsMmKgbdv1OAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDq0dO/btsHdu6585x/SKN9NFlBKXsMmKgbdv1OAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDq0dO%2FbtsHdu6585x%2FSKN9NFlBKXsMmKgbdv1OAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;343&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;343&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국어 버전 기준, '그리드 스냅'을 검색하여 키를 할당 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Alt + 마우스 드래그로 지정하고 싶은데 키 바인딩 커스텀에서 마우스 이벤트 넣는 방법을 모르겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bylTOY/btsHcuzKGrA/V6k5c0OQRYfsJZfESaQx70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bylTOY/btsHcuzKGrA/V6k5c0OQRYfsJZfESaQx70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bylTOY/btsHcuzKGrA/V6k5c0OQRYfsJZfESaQx70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbylTOY%2FbtsHcuzKGrA%2FV6k5c0OQRYfsJZfESaQx70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1162&quot; height=&quot;280&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 오브젝트를 뷰에 스냅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택한 오브젝트를 현재 뷰포트 카메라 Transform으로 맞추는 기능입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트 선택 후, 우측 마우스 메뉴를 통해 실행 가능하지만, 기본적으로 키 바인딩이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 유니티 엔진에서 카메라 위치 제어 할 때 자주 사용하는 기능이기 때문에 키 바인딩하면 유용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQU0VH/btsHcTTApjY/JEferRwLu1K69Jd8eETuo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQU0VH/btsHcTTApjY/JEferRwLu1K69Jd8eETuo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQU0VH/btsHcTTApjY/JEferRwLu1K69Jd8eETuo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQU0VH%2FbtsHcTTApjY%2FJEferRwLu1K69Jd8eETuo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;510&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;'오브젝트를 뷰에 스냅'&lt;/b&gt;을 검색 후, 유니티와 동일하게&lt;b&gt; 'Ctrl+Shift+F'&lt;/b&gt;로 세팅합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0TgHM/btsHgpcrDuE/F2ifLb8l1K2LqS95EVSAqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0TgHM/btsHgpcrDuE/F2ifLb8l1K2LqS95EVSAqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0TgHM/btsHgpcrDuE/F2ifLb8l1K2LqS95EVSAqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0TgHM%2FbtsHgpcrDuE%2FF2ifLb8l1K2LqS95EVSAqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1124&quot; height=&quot;272&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이미 다른 기능에 키가 바인딩되어 있습니다, '블루프린트에서 찾기' 기능은 별로 사용하지 않을 것 같으니, 삭제하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk9dMp/btsHcL2f3Ak/EEEN097cXNtjwkgyLntge1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk9dMp/btsHcL2f3Ak/EEEN097cXNtjwkgyLntge1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk9dMp/btsHcL2f3Ak/EEEN097cXNtjwkgyLntge1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk9dMp%2FbtsHcL2f3Ak%2FEEEN097cXNtjwkgyLntge1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1107&quot; height=&quot;301&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Redo (다시 실행) 키 변경&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Undo의 반대 기능입니다. Undo 이벤트를 다시 되돌릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;b&gt;'Ctrl + Y'&lt;/b&gt;로 바인딩되어 있지만 익숙하지 않으니 유니티 엔진과 동일하게&lt;b&gt; 'Ctrl + Shift + Z'&lt;/b&gt; 로 바인딩했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br8fX4/btsHea1yBKM/fQ029lSsenvRANYQUcOcTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br8fX4/btsHea1yBKM/fQ029lSsenvRANYQUcOcTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br8fX4/btsHea1yBKM/fQ029lSsenvRANYQUcOcTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr8fX4%2FbtsHea1yBKM%2FfQ029lSsenvRANYQUcOcTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1119&quot; height=&quot;388&quot; data-origin-width=&quot;1119&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;레퍼런스&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.domestika.org/en/blog/4733-basic-keyboard-shortcuts-for-unreal-engine-4-and-5&quot;&gt;https://www.domestika.org/en/blog/4733-basic-keyboard-shortcuts-for-unreal-engine-4-and-5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=cnKp1HDYaJk&amp;amp;ab_channel=PendingKill&quot;&gt;https://www.youtube.com/watch?v=cnKp1HDYaJk&amp;amp;ab_channel=PendingKill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Unreal</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/168</guid>
      <comments>https://darkcatgame.tistory.com/168#entry168comment</comments>
      <pubDate>Tue, 7 May 2024 22:31:24 +0900</pubDate>
    </item>
    <item>
      <title>3 Point Lighting</title>
      <link>https://darkcatgame.tistory.com/167</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blv3v7/btsG6eKp0RD/wZxqbSs0r8slOqcB8KPSM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blv3v7/btsG6eKp0RD/wZxqbSs0r8slOqcB8KPSM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blv3v7/btsG6eKp0RD/wZxqbSs0r8slOqcB8KPSM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblv3v7%2FbtsG6eKp0RD%2FwZxqbSs0r8slOqcB8KPSM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1214&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mbqfI/btsG7oyYqcE/SbK86cFN7Ni8MiFQI63zyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mbqfI/btsG7oyYqcE/SbK86cFN7Ni8MiFQI63zyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mbqfI/btsG7oyYqcE/SbK86cFN7Ni8MiFQI63zyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmbqfI%2FbtsG7oyYqcE%2FSbK86cFN7Ni8MiFQI63zyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1249&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1249&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;레퍼런스 : Last of us part 2 - joel&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2221&quot; data-origin-height=&quot;1244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qQKnv/btsG8SMEo4X/dx2sofPgEwgakLfglD7bBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qQKnv/btsG8SMEo4X/dx2sofPgEwgakLfglD7bBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qQKnv/btsG8SMEo4X/dx2sofPgEwgakLfglD7bBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqQKnv%2FbtsG8SMEo4X%2Fdx2sofPgEwgakLfglD7bBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2221&quot; height=&quot;1244&quot; data-origin-width=&quot;2221&quot; data-origin-height=&quot;1244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;레벨 &amp;amp; 라이팅 구성&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3 point light 배치&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2131&quot; data-origin-height=&quot;1973&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ucQB8/btsG55fO1cg/OfAkoARNw77Gq9JNqxaga0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ucQB8/btsG55fO1cg/OfAkoARNw77Gq9JNqxaga0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ucQB8/btsG55fO1cg/OfAkoARNw77Gq9JNqxaga0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FucQB8%2FbtsG55fO1cg%2FOfAkoARNw77Gq9JNqxaga0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;488&quot; data-origin-width=&quot;2131&quot; data-origin-height=&quot;1973&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;&lt;b&gt;Key Light&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;&lt;b&gt;Fill Light&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;&lt;b&gt;Rim Light&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2270&quot; data-origin-height=&quot;1726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z9KUa/btsG53WDsZF/Ryns8bWdRFYjhRJ750IDKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z9KUa/btsG53WDsZF/Ryns8bWdRFYjhRJ750IDKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z9KUa/btsG53WDsZF/Ryns8bWdRFYjhRJ750IDKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz9KUa%2FbtsG53WDsZF%2FRyns8bWdRFYjhRJ750IDKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2270&quot; height=&quot;1726&quot; data-origin-width=&quot;2270&quot; data-origin-height=&quot;1726&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2273&quot; data-origin-height=&quot;1730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKcrpf/btsG80KD0al/lNmtqekkVwTa8jeIIgXU70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKcrpf/btsG80KD0al/lNmtqekkVwTa8jeIIgXU70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKcrpf/btsG80KD0al/lNmtqekkVwTa8jeIIgXU70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKcrpf%2FbtsG80KD0al%2FlNmtqekkVwTa8jeIIgXU70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2273&quot; height=&quot;1730&quot; data-origin-width=&quot;2273&quot; data-origin-height=&quot;1730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2241&quot; data-origin-height=&quot;1653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JqqLL/btsG74UyAWt/3ROoI9dvCqJf0a46g8rPrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JqqLL/btsG74UyAWt/3ROoI9dvCqJf0a46g8rPrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JqqLL/btsG74UyAWt/3ROoI9dvCqJf0a46g8rPrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJqqLL%2FbtsG74UyAWt%2F3ROoI9dvCqJf0a46g8rPrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2241&quot; height=&quot;1653&quot; data-origin-width=&quot;2241&quot; data-origin-height=&quot;1653&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;라이팅 구성&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;Diffuse&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;&lt;b&gt;Specular&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2165&quot; data-origin-height=&quot;1656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PhEkP/btsG9b6e1b2/kAxnF77VwnhBDOYotwWlB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PhEkP/btsG9b6e1b2/kAxnF77VwnhBDOYotwWlB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PhEkP/btsG9b6e1b2/kAxnF77VwnhBDOYotwWlB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPhEkP%2FbtsG9b6e1b2%2FkAxnF77VwnhBDOYotwWlB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2165&quot; height=&quot;1656&quot; data-origin-width=&quot;2165&quot; data-origin-height=&quot;1656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLMCuU/btsG542h13Y/Zcy45pGF9o8M8xkcOiw6Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLMCuU/btsG542h13Y/Zcy45pGF9o8M8xkcOiw6Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLMCuU/btsG542h13Y/Zcy45pGF9o8M8xkcOiw6Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLMCuU%2FbtsG542h13Y%2FZcy45pGF9o8M8xkcOiw6Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2200&quot; height=&quot;1764&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Lighting Art</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/167</guid>
      <comments>https://darkcatgame.tistory.com/167#entry167comment</comments>
      <pubDate>Thu, 2 May 2024 22:36:14 +0900</pubDate>
    </item>
    <item>
      <title>URP GPU Instancing와 SRP Batcher 성능 비교</title>
      <link>https://darkcatgame.tistory.com/166</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRP Batcher는 RenderState 호출을 배칭하여 렌더콜 API 호출 수를 줄여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 DrawCalls 호출 수를 줄이지 않기 때문에 뷰에 Renderer 숫자가 많으면 여전히 CPU 부하 발생 위험이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU Instancing은 렌더링에 필요한 API 정보를 GPU 버퍼에 저장하여 반복 렌더링 명령을 하는 방식으로 CPU에서 발생하는 렌더콜 숫자를 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 GPU Instancing과 SRP Batcher 성능 비교 정리 자료입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;테스트 환경&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl1ln8/btsGTCRCLrA/3JXWYs1kYk6E7e3AdfdqW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl1ln8/btsGTCRCLrA/3JXWYs1kYk6E7e3AdfdqW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl1ln8/btsGTCRCLrA/3JXWYs1kYk6E7e3AdfdqW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl1ln8%2FbtsGTCRCLrA%2F3JXWYs1kYk6E7e3AdfdqW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2252&quot; height=&quot;1022&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;1022&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메쉬 3종, Material 2 종으로 구성된 씬입니다. (URP Lit 쉐이더 기반)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mesh &amp;amp; Material 세팅 조합은 별도로 섞지 않아 총 3개의 Instancing이 나오는 환경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Batches는 에디터 Stat에 약 2300개가 나오게 세팅했습니다. (막 복사해서 정확한 숫자 모름)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 환경에서 SRP Batcher를 지원하는 쉐이더는 GPU Instancing이 비활성화됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 스크립트에서 Graphics.DrawMeshInstanced 함수를 활용해 GPU Instancing이 작동되도록 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트는 포스팅 맨 아래 첨부합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과 정리&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biXPoK/btsGVvxhtvQ/EJFYyIvGoFKDmZhevAe9m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biXPoK/btsGVvxhtvQ/EJFYyIvGoFKDmZhevAe9m1/img.png&quot; data-origin-width=&quot;2072&quot; data-origin-height=&quot;940&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.3449%; margin-right: 10px;&quot; data-widthpercent=&quot;46.89&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biXPoK/btsGVvxhtvQ/EJFYyIvGoFKDmZhevAe9m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiXPoK%2FbtsGVvxhtvQ%2FEJFYyIvGoFKDmZhevAe9m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2072&quot; height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4InUz/btsGUV4fyKR/fdWUju1cYYeJp66C5HK9sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4InUz/btsGUV4fyKR/fdWUju1cYYeJp66C5HK9sK/img.png&quot; data-origin-width=&quot;2227&quot; data-origin-height=&quot;892&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.4923%;&quot; data-widthpercent=&quot;53.11&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4InUz/btsGUV4fyKR/fdWUju1cYYeJp66C5HK9sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4InUz%2FbtsGUV4fyKR%2FfdWUju1cYYeJp66C5HK9sK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2227&quot; height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : GPU Instancing / 우 : SRP Batcher&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJgZhq/btsGVckqzVu/KgGOCTYtJhu4CkPyVsfz1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJgZhq/btsGVckqzVu/KgGOCTYtJhu4CkPyVsfz1k/img.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;533&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.9239%; margin-right: 10px;&quot; data-widthpercent=&quot;51.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJgZhq/btsGVckqzVu/KgGOCTYtJhu4CkPyVsfz1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJgZhq%2FbtsGVckqzVu%2FKgGOCTYtJhu4CkPyVsfz1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1140&quot; height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd61Gl/btsGTEBVyRx/VoqT9bgH4JysoCHUOE9Ey0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd61Gl/btsGTEBVyRx/VoqT9bgH4JysoCHUOE9Ey0/img.png&quot; data-origin-width=&quot;1137&quot; data-origin-height=&quot;565&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.9133%;&quot; data-widthpercent=&quot;48.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd61Gl/btsGTEBVyRx/VoqT9bgH4JysoCHUOE9Ey0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd61Gl%2FbtsGTEBVyRx%2FVoqT9bgH4JysoCHUOE9Ey0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1137&quot; height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : GPU Instancing / 우 : SRP Batcher&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 128px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 36.5891%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 31.4728%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;SRP Batcher&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.938%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;GPU Instancing&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 36.5891%; height: 20px;&quot;&gt;&lt;b&gt;프로파일러 - CPU 메인쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.4728%; height: 20px; text-align: center;&quot;&gt;11.84 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;ms&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.938%; height: 20px; text-align: center;&quot;&gt;4.28 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 36.5891%; height: 20px;&quot;&gt;&lt;b&gt;프로파일러 - CPU 렌더쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.4728%; height: 20px; text-align: center;&quot;&gt;10.69 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;ms&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.938%; height: 20px; text-align: center;&quot;&gt;2.39 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 36.5891%; height: 20px;&quot;&gt;&lt;b&gt;프로팡리러 - CPU 렌더콜 총합&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.4728%; height: 20px; text-align: center;&quot;&gt;17.10 ms&lt;/td&gt;
&lt;td style=&quot;width: 31.938%; height: 20px; text-align: center;&quot;&gt;5.26 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 36.5891%; height: 17px;&quot;&gt;&lt;b&gt;RenderDoc - Draw calls&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.4728%; height: 17px; text-align: center;&quot;&gt;2292&lt;/td&gt;
&lt;td style=&quot;width: 31.938%; height: 17px; text-align: center;&quot;&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 36.5891%; height: 17px;&quot;&gt;&lt;b&gt;RenderDoc - API calls&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.4728%; height: 17px; text-align: center;&quot;&gt;3558&lt;/td&gt;
&lt;td style=&quot;width: 31.938%; height: 17px; text-align: center;&quot;&gt;157&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 36.5891%; height: 17px;&quot;&gt;&lt;b&gt;RenderDoc - GPU Buffer + texture load&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.4728%; height: 17px; text-align: center;&quot;&gt;22.19 MB&lt;/td&gt;
&lt;td style=&quot;width: 31.938%; height: 17px; text-align: center;&quot;&gt;24.19 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU Instancing이 압도적으로 좋은 성능이 나왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이런 GPU 드로우 방식은 다양한 종류의 메쉬 &amp;amp; Material 지원이 제한적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후 GPU Instancing 외에 InDirectDraw등 다른 GPU 드로우 방식을 테스트 해볼 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;GPU Instancing 드로우 스크립트&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1713965310837&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GPUInstancingManager : MonoBehaviour
{
    private class InstanceData
    {
        public MeshRenderer meshRenderer;
        public Mesh mesh;
        public Material material;
        public Matrix4x4[] matrices;
        public int meshCount = 0;

        public InstanceData(MeshRenderer meshRenderer, Mesh mesh, Material material)
        {
            this.meshRenderer = meshRenderer;
            this.mesh = mesh;   
            this.material = material;
        }
    };

    private struct MatrixData
    {
        public List&amp;lt;Matrix4x4&amp;gt; matrices;
        public MatrixData(List&amp;lt;Matrix4x4&amp;gt; matrices)
        {
            this.matrices = matrices;
        }
    };

    private List&amp;lt;InstanceData&amp;gt; _instanceDatas;

    void Start()
    {
        _instanceDatas = InitMeshInstanced();
        Debug.Log(&quot;Instance Count : &quot; + _instanceDatas.Count);
    }

    // Update is called once per frame
    void Update()
    {
        for(int i=0;i&amp;lt; _instanceDatas.Count;i++) 
        {
            InstanceData instanceData = _instanceDatas[i];
            Graphics.DrawMeshInstanced(instanceData.mesh, 0, instanceData.material, instanceData.matrices, instanceData.meshCount);
        }
    }

    private List&amp;lt;InstanceData&amp;gt; InitMeshInstanced()
    {
        List&amp;lt;InstanceData&amp;gt; instanceDatas = new List&amp;lt;InstanceData&amp;gt;();
        List&amp;lt;MatrixData&amp;gt; matrixDatas = new List&amp;lt;MatrixData&amp;gt;();
        MeshRenderer[] meshRenderers = transform.GetComponentsInChildren&amp;lt;MeshRenderer&amp;gt;();

        for(int i=0; i&amp;lt; meshRenderers.Length; i++)
        {
            MeshRenderer renderer = meshRenderers[i];
            if (!renderer) continue;
            Material material = renderer.sharedMaterial;
            if (!material) continue;
            MeshFilter meshFilter = renderer.GetComponent&amp;lt;MeshFilter&amp;gt;();
            if(!meshFilter) continue;
            Mesh mesh = meshFilter.sharedMesh;
            if (!mesh) continue;
            Matrix4x4 matrix = renderer.transform.localToWorldMatrix;

            bool isExistsData = false;
            for(int j=0;j&amp;lt; instanceDatas.Count;j++)
            {
                InstanceData instanceData = instanceDatas[j];
                if(instanceData.mesh == mesh &amp;amp;&amp;amp; instanceData.material == material) 
                {
                    isExistsData = true;
                    matrixDatas[j].matrices.Add(matrix);
                    break;
                }
            }

            if(!isExistsData)
            {
                material.enableInstancing = true;
                instanceDatas.Add(new InstanceData(renderer, mesh, material));
                List&amp;lt;Matrix4x4&amp;gt; matrices = new List&amp;lt;Matrix4x4&amp;gt;();
                matrices.Add(matrix);
                matrixDatas.Add(new MatrixData(matrices));
            }
            Destroy(renderer.gameObject);
        }

        for(int i=0;i&amp;lt; instanceDatas.Count;i++)
        {
            instanceDatas[i].matrices = matrixDatas[i].matrices.ToArray();
            instanceDatas[i].meshCount = matrixDatas[i].matrices.Count;
        }

        return instanceDatas;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/166</guid>
      <comments>https://darkcatgame.tistory.com/166#entry166comment</comments>
      <pubDate>Wed, 24 Apr 2024 22:28:39 +0900</pubDate>
    </item>
    <item>
      <title>Chiaro Scuro - 키아로 스쿠로</title>
      <link>https://darkcatgame.tistory.com/165</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;핵심&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밝음과 어두움 라이팅 대비를 활용해 물체 형태(Shape) 표현&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mWoRe/btsGEpFC6oY/Mu8dIa95jfMVGyaKlJf1BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mWoRe/btsGEpFC6oY/Mu8dIa95jfMVGyaKlJf1BK/img.png&quot; data-alt=&quot;https://nofilmschool.com/chiaroscuro-lighting&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mWoRe/btsGEpFC6oY/Mu8dIa95jfMVGyaKlJf1BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmWoRe%2FbtsGEpFC6oY%2FMu8dIa95jfMVGyaKlJf1BK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;747&quot; height=&quot;418&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://nofilmschool.com/chiaroscuro-lighting&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;언리얼 엔진 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HiJRM/btsGGQhQQaJ/77CNyAb9Judq3fWjKGVtr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HiJRM/btsGGQhQQaJ/77CNyAb9Judq3fWjKGVtr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HiJRM/btsGGQhQQaJ/77CNyAb9Judq3fWjKGVtr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHiJRM%2FbtsGGQhQQaJ%2F77CNyAb9Judq3fWjKGVtr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1156&quot; height=&quot;609&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이트 1개만 사용한 결과.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체의 형태가 표현되지 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Chiaro Scuro 반영&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O1Aeo/btsGEsCp0K1/9WQM0ZhzvrYECHTsp1TexK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O1Aeo/btsGEsCp0K1/9WQM0ZhzvrYECHTsp1TexK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O1Aeo/btsGEsCp0K1/9WQM0ZhzvrYECHTsp1TexK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO1Aeo%2FbtsGEsCp0K1%2F9WQM0ZhzvrYECHTsp1TexK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1161&quot; height=&quot;609&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJuOXk/btsGGmHXVW4/GK1jnKdnSQuVFp7kdyphA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJuOXk/btsGGmHXVW4/GK1jnKdnSQuVFp7kdyphA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJuOXk/btsGGmHXVW4/GK1jnKdnSQuVFp7kdyphA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJuOXk%2FbtsGGmHXVW4%2FGK1jnKdnSQuVFp7kdyphA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1147&quot; height=&quot;658&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;658&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체 피사체 뒤 벽에 추가 라이트를 반영하여 기존 어두운 부분에 대한 명암 대비 구성하여 Chiaro Scuro 이론 구현.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 라이팅을 활용해 구체의 형태를 제대로 표현할 수 있게 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Lighting Art</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/165</guid>
      <comments>https://darkcatgame.tistory.com/165#entry165comment</comments>
      <pubDate>Tue, 16 Apr 2024 23:44:42 +0900</pubDate>
    </item>
    <item>
      <title>Value Contrast Focal Point Case Study</title>
      <link>https://darkcatgame.tistory.com/164</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이팅 명도 대비를 활용한 Focal Point 구성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Case Study - Dark knight 영화 장면&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Good Case&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;537&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7T420/btsGHcSk8ZU/YaPCKdMLWGplUjPcmLqgi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7T420/btsGHcSk8ZU/YaPCKdMLWGplUjPcmLqgi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7T420/btsGHcSk8ZU/YaPCKdMLWGplUjPcmLqgi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7T420%2FbtsGHcSk8ZU%2FYaPCKdMLWGplUjPcmLqgi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1162&quot; height=&quot;537&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로 밝은 배경에 어두운 복장의 인물에게 Focal Point 구성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Bad Case&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9Ls9X/btsGFAT6jLH/mLqu76kl7BRTYzU7nyM5dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9Ls9X/btsGFAT6jLH/mLqu76kl7BRTYzU7nyM5dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9Ls9X/btsGFAT6jLH/mLqu76kl7BRTYzU7nyM5dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9Ls9X%2FbtsGFAT6jLH%2FmLqu76kl7BRTYzU7nyM5dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1151&quot; height=&quot;510&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 영화 프레임에 인물 복장만 밝은 색으로 변경.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흰색 옷이 상대적으로 높은 Value라서 Focal Point가 작동되긴 하지만, Good Case와 비교해서 아쉬워짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Lighting Art</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/164</guid>
      <comments>https://darkcatgame.tistory.com/164#entry164comment</comments>
      <pubDate>Tue, 16 Apr 2024 23:36:55 +0900</pubDate>
    </item>
    <item>
      <title>단일 Material 공유가 SRPBatcher 성능에 영향을 주는가?</title>
      <link>https://darkcatgame.tistory.com/163</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRPBatcher는 SetPass Calls API를 배칭(묶어)하여 CPU 렌더 호출 비용을 최적화하는 시스템입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 SRPBatcher를 사용해도 렌더링하는 오브젝트가 많을 경우에는 여전히 CPU 렌더 호출 비용이 높은 상황이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 CPU 렌더 호출 비용을 조금이라도 최적화하기 위해서 SRPBatcher가 효율적으로 동작할 수 있는 환경을 테스트해본 내용을 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 주제는 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SRP Batcher가 동작하는 환경에서 단일 Material을 공유하는게 SRP Batcher에 효과가 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 결과 미리 요약&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 Material 공유 여부는 SRP Batcher 및 CPU 렌더 호출 비용에 약간의 성능 향상이 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Material 1개 vs Mateirla 300개 기준 CPU 렌더콜 성능 1 ~ 2ms 차이 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;쉐이더의 CBuffer 변수 갯수는 SRP Batcher 및 CPU 렌더 호출 비용에 유의미한 영향 없음.&lt;/li&gt;
&lt;li&gt;CPU 렌더 호출 비용을 줄이는 직접적인 방법은 Renderer를 줄여 드로우콜을 낮추는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 환경&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Unity 2022.1.22f1 / URP 13.1.8&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;갤럭시s10e / ARM Mali-G76 / Vulkan&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;320p / 30fps&amp;nbsp;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Unity Profiler / RenderDoc&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Material 1종 vs Material 개별 Instancing&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HRBdM/btsGB2Rkz31/iFBnRqISmRXVkjzVkVlqV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HRBdM/btsGB2Rkz31/iFBnRqISmRXVkjzVkVlqV0/img.png&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;697&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.441%; margin-right: 10px;&quot; data-widthpercent=&quot;50.02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HRBdM/btsGB2Rkz31/iFBnRqISmRXVkjzVkVlqV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHRBdM%2FbtsGB2Rkz31%2FiFBnRqISmRXVkjzVkVlqV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1118&quot; height=&quot;697&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;702&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.3962%;&quot; data-widthpercent=&quot;49.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQwvf6%2FbtsGDDb40G0%2FJnZwOzC2ooURvvNdeUK62K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Renderer 300개 / Mesh 1종 / 좌 - Material 1종, 우 - Material 개별 Instancing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kEXfW/btsGB9Qk88A/ucxqq7TskhxqXZjjwkX5S0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kEXfW/btsGB9Qk88A/ucxqq7TskhxqXZjjwkX5S0/img.png&quot; data-origin-width=&quot;2215&quot; data-origin-height=&quot;948&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;54.08&quot; style=&quot;width: 53.4536%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kEXfW/btsGB9Qk88A/ucxqq7TskhxqXZjjwkX5S0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkEXfW%2FbtsGB9Qk88A%2Fucxqq7TskhxqXZjjwkX5S0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2215&quot; height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; data-origin-width=&quot;2198&quot; data-origin-height=&quot;1108&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.3836%;&quot; data-widthpercent=&quot;45.92&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxair8%2FbtsGCXWaJ8k%2FE03LZJGZ59KnSe4cn1mG01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2198&quot; height=&quot;1108&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 - Material 1종, 우 - Material 개별 Instancing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 74px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;최소 / 최대 ms&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Material 1종&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Material 개별 Instancing&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;메인 쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;5.7 / 8.53&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;5.79 / 9.82&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;렌더 쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;3.24 / 3.77&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;4.22 / 5.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;CPU 렌더 콜 총합&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 17px;&quot;&gt;7.74 / 10.42&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 17px;&quot;&gt;8.69 / 12.80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Material 1종 공유하는 것과, 모든 Renderer가 다른 Material을 Instancing하는 것은 CPU 렌더콜 성능에 약간의 차이가 확인되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트에 사용한 쉐이더는 URP Lit이며, 쉐이더 복잡도나 메쉬 버텍스 카운트에 따라 결과 차이가 변화될 여지가 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Material 개별 Instancing vs Material 개별 Instancing + 추가 프로퍼티 변화&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blltlr/btsGCvyNsBZ/xSRfG3ZjpFUvS5f4s5T4Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blltlr/btsGCvyNsBZ/xSRfG3ZjpFUvS5f4s5T4Zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blltlr/btsGCvyNsBZ/xSRfG3ZjpFUvS5f4s5T4Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblltlr%2FbtsGCvyNsBZ%2FxSRfG3ZjpFUvS5f4s5T4Zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;949&quot; height=&quot;170&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 테스트는 Material 개별 Instancing은 동일하지만 1개의 Case에는 Material 프로퍼티 변화를 추가로 반영했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; style=&quot;width: 49.8171%; margin-right: 10px;&quot; data-widthpercent=&quot;50.4&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;702&quot; data-origin-width=&quot;1125&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQwvf6%2FbtsGDDb40G0%2FJnZwOzC2ooURvvNdeUK62K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDx8p5/btsGCpr1fXH/9fln72O3iKQTjCmDXJpCv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDx8p5/btsGCpr1fXH/9fln72O3iKQTjCmDXJpCv0/img.png&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;702&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0201%;&quot; data-widthpercent=&quot;49.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDx8p5/btsGCpr1fXH/9fln72O3iKQTjCmDXJpCv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDx8p5%2FbtsGCpr1fXH%2F9fln72O3iKQTjCmDXJpCv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1107&quot; height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Renderer 300개 / Mesh 1종 / 좌 - Material 개별 Instancing, 우 - Material 개별 Instancing + 추가 프로퍼티 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; data-widthpercent=&quot;50.56&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;1108&quot; data-origin-width=&quot;2198&quot; style=&quot;width: 49.9701%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxair8%2FbtsGCXWaJ8k%2FE03LZJGZ59KnSe4cn1mG01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2198&quot; height=&quot;1108&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSJLhy/btsGEj5xdGI/cFvR2IcpvgaWdNFhftI111/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSJLhy/btsGEj5xdGI/cFvR2IcpvgaWdNFhftI111/img.png&quot; data-origin-width=&quot;2165&quot; data-origin-height=&quot;1116&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.8671%;&quot; data-widthpercent=&quot;49.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSJLhy/btsGEj5xdGI/cFvR2IcpvgaWdNFhftI111/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSJLhy%2FbtsGEj5xdGI%2FcFvR2IcpvgaWdNFhftI111%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2165&quot; height=&quot;1116&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 - Material 개별 Instancing, 우 - Material 개별 Instancing + 추가 프로퍼티 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 74px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;최소 / 최대 ms&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Material 개별 Instancing&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Instancing + 추가 프로퍼티 변화&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;메인 쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;5.79 / 9.82&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;5.43 / 8.23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;렌더 쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;4.22 / 5.95&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;4.17 / 5.01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;CPU 렌더 콜 총합&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;8.69 / 12.80&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;8.04 / 11.45&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일하게 Material 개별 Instancing 환경에서 CBuffer에 선언된 Material 프로퍼티 변화 차이만 측정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유의미한 차이는 없으며 심지어, 추가 프로퍼티 변화한 케이스가 더 성능이 가볍게 측정되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Material 개별 Instancing vs 개별 Instancing + Reflection Probe Blending&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1742&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0zHjK/btsGB3pdqju/aV6NxD651QYVvNRcUOnLA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0zHjK/btsGB3pdqju/aV6NxD651QYVvNRcUOnLA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0zHjK/btsGB3pdqju/aV6NxD651QYVvNRcUOnLA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0zHjK%2FbtsGB3pdqju%2FaV6NxD651QYVvNRcUOnLA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1742&quot; height=&quot;606&quot; data-origin-width=&quot;1742&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 테스트는 개별 Instancing 환경에서 5개의 Reflection Probe를 걸쳐서 배치했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 Reflection Probe Blending 기능까지 활성화했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더피쳐 단계에서 글로벌 프로퍼티로 텍스처 정보를 전달하는 것에 대한 차이를 측정하기 위함입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; style=&quot;width: 48.1995%; margin-right: 10px;&quot; data-origin-width=&quot;2198&quot; data-origin-height=&quot;1108&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;48.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxair8%2FbtsGCXWaJ8k%2FE03LZJGZ59KnSe4cn1mG01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2198&quot; height=&quot;1108&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmFwAX/btsGCcsMokd/wjsuVHUjtdhmWrpeqXJdTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmFwAX/btsGCcsMokd/wjsuVHUjtdhmWrpeqXJdTK/img.png&quot; data-origin-width=&quot;2255&quot; data-origin-height=&quot;1082&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.6377%;&quot; data-widthpercent=&quot;51.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmFwAX/btsGCcsMokd/wjsuVHUjtdhmWrpeqXJdTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmFwAX%2FbtsGCcsMokd%2FwjsuVHUjtdhmWrpeqXJdTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2255&quot; height=&quot;1082&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 - Material 개별 Instancing, 우 - Material 개별 Instancing + Reflection Probe Blending&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 74px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;최소 / 최대 ms&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Material 개별 Instancing&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Instancing + Refl ProbeBlending&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;메인 쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;5.79 / 9.82&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;6.93 / 11.32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;렌더 쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;4.22 / 5.95&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;4.76 / 5.16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;CPU 렌더 콜 총합&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;8.69 / 12.80&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;8.126 / 13.04&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 Material 개별 Instancing 환경에서 Reflection Probe Blending 테스트는 유의미한 차이가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Material 개별 Instancing, Renderer 100개 vs 300개&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbV9aC/btsGCaBISSW/TWn72go4ZGpIG7z6h8NF3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbV9aC/btsGCaBISSW/TWn72go4ZGpIG7z6h8NF3k/img.png&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;670&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.9712%; margin-right: 10px;&quot; data-widthpercent=&quot;50.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbV9aC/btsGCaBISSW/TWn72go4ZGpIG7z6h8NF3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbV9aC%2FbtsGCaBISSW%2FTWn72go4ZGpIG7z6h8NF3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1098&quot; height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; style=&quot;width: 48.8661%;&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;702&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;49.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQwvf6/btsGDDb40G0/JnZwOzC2ooURvvNdeUK62K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQwvf6%2FbtsGDDb40G0%2FJnZwOzC2ooURvvNdeUK62K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Material 개별 Instancing / 좌 - Rendere 100개, 우 - Renderer 300&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9R4YW/btsGEi6EaVU/8556VLnAfwKs2RNwToMCXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9R4YW/btsGEi6EaVU/8556VLnAfwKs2RNwToMCXk/img.png&quot; data-origin-width=&quot;2135&quot; data-origin-height=&quot;1066&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.6549%; margin-right: 10px;&quot; data-widthpercent=&quot;50.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9R4YW/btsGEi6EaVU/8556VLnAfwKs2RNwToMCXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9R4YW%2FbtsGEi6EaVU%2F8556VLnAfwKs2RNwToMCXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2135&quot; height=&quot;1066&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; style=&quot;width: 49.1823%;&quot; data-widthpercent=&quot;49.76&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;1108&quot; data-origin-width=&quot;2198&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xair8/btsGCXWaJ8k/E03LZJGZ59KnSe4cn1mG01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxair8%2FbtsGCXWaJ8k%2FE03LZJGZ59KnSe4cn1mG01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2198&quot; height=&quot;1108&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Material 개별 Instancing / 좌 - Rendere 100개, 우 - Renderer 300&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 74px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;최소 / 최대 ms&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Renderer 100개&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Renderer 300개&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;메인 쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;3.73&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;5.79 / 9.82&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;렌더 쓰레드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;2.01&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;4.22 / 5.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;CPU 렌더 콜 총합&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;5.16&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;8.69 / 12.80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 단순히 Renderer 갯수 별로 측정하고 드로우콜 API 차이를 테스트해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 정직하게 드로우콜 API 차이가 CPU 렌더콜 성능에 직접적으로 영향을 주는 것이 확인되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CBDnI/btsGCrpHuxr/jbq0s9ADtclxhZcwzBTRsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CBDnI/btsGCrpHuxr/jbq0s9ADtclxhZcwzBTRsk/img.png&quot; data-origin-width=&quot;1923&quot; data-origin-height=&quot;949&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.5857%; margin-right: 10px;&quot; data-widthpercent=&quot;50.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CBDnI/btsGCrpHuxr/jbq0s9ADtclxhZcwzBTRsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCBDnI%2FbtsGCrpHuxr%2Fjbq0s9ADtclxhZcwzBTRsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1923&quot; height=&quot;949&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s3z3H/btsGFrooPnB/wj47enFxd5eKSRwQcSSQM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s3z3H/btsGFrooPnB/wj47enFxd5eKSRwQcSSQM1/img.png&quot; data-origin-width=&quot;2063&quot; data-origin-height=&quot;1025&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.2515%;&quot; data-widthpercent=&quot;49.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s3z3H/btsGFrooPnB/wj47enFxd5eKSRwQcSSQM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs3z3H%2FbtsGFrooPnB%2Fwj47enFxd5eKSRwQcSSQM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2063&quot; height=&quot;1025&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Material 개별 Instancing / 좌 - Rendere 100개, 우 - Renderer 300&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더독 확인 결과 특이사항 없습니다.&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>SetPass Calls</category>
      <category>SRPBatcher</category>
      <category>드로우콜</category>
      <category>유니티</category>
      <category>최적화</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/163</guid>
      <comments>https://darkcatgame.tistory.com/163#entry163comment</comments>
      <pubDate>Mon, 15 Apr 2024 21:54:44 +0900</pubDate>
    </item>
    <item>
      <title>유니티 URP RendererFeature 활성화 제어 스크립트</title>
      <link>https://darkcatgame.tistory.com/162</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 URP 환경에서 Universal Renderer Data에 다양한 렌더피처를 추가 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 커스텀 렌더 패스로 활용하기 때문에 그래픽 퀄리티 옵션 제어가 필요한 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서 C# 스크립트에서 Universal Renderer Data에 접근해서 원한느 RendererFeature를 활성화 &amp;amp; 비활성화하는 스크립트를 소개합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhqsDD/btsDzuPHn1G/mCbclvOjwH8KGO7lULIPSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhqsDD/btsDzuPHn1G/mCbclvOjwH8KGO7lULIPSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhqsDD/btsDzuPHn1G/mCbclvOjwH8KGO7lULIPSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhqsDD%2FbtsDzuPHn1G%2FmCbclvOjwH8KGO7lULIPSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;379&quot; height=&quot;675&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;샘플 코드 &amp;amp; 설명&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로젝트 환경&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity 2021.3.30f1 (유니티 버전에 따라 API 변경될 수 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1705402694261&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;
using System.Reflection;

public class RendererFeatureControllSample : MonoBehaviour 
{
    /* 사용 샘플 코드
     * SetActiveRendererFeature&amp;lt;FXAARenderFeature&amp;gt;(toggle); 
     */
    public void SetActiveRendererFeature&amp;lt;T&amp;gt;(bool active) where T : ScriptableRendererFeature
    {
        // URP Asset의 Renderer List에서 0번 인덱스 RendererData 참조
        ScriptableRendererData rendererData = GetRendererData(0);
        if (rendererData == null) return;
   
        List&amp;lt;ScriptableRendererFeature&amp;gt; rendererFeatures = rendererData.rendererFeatures;
        if (rendererFeatures == null || rendererFeatures.Count &amp;lt;= 0) return;
 
        for (int i = 0; i &amp;lt; rendererFeatures.Count; i++)
        {
            ScriptableRendererFeature rendererFeature = rendererFeatures[i];
            if (!rendererFeature) continue;
            if (rendererFeature is T) rendererFeature.SetActive(active);
        }
        #if UNITY_EDITOR
        rendererData.SetDirty();
        #endif
    }


    public ScriptableRendererData GetRendererData(int rendererIndex = 0)
    {
        // 현재 Quality 옵션에 세팅된 URP Asset 참조
        UniversalRenderPipelineAsset pipelineAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
        if (!pipelineAsset) return null;
  
        // URP Renderer List 리플렉션 참조 (Internal 변수라서 그냥 참조 불가능)
        FieldInfo propertyInfo = pipelineAsset.GetType().GetField(&quot;m_RendererDataList&quot;, BindingFlags.Instance | BindingFlags.NonPublic);
        ScriptableRendererData[] rendererDatas = (ScriptableRendererData[])propertyInfo.GetValue(pipelineAsset);
        if (rendererDatas == null || rendererDatas.Length &amp;lt;= 0) return null;
        if (rendererIndex &amp;lt; 0 || rendererDatas.Length &amp;lt;= rendererIndex) return null;

        return rendererDatas[rendererIndex];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;현재&amp;nbsp;Quality&amp;nbsp;옵션에&amp;nbsp;세팅된&amp;nbsp;URP&amp;nbsp;Asset&amp;nbsp;참조&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1705402772125&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UniversalRenderPipelineAsset pipelineAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
if (!pipelineAsset) return null;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co3nhq/btsDxS4rqJx/G0aI8HTHmbiW9DICiNkOzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co3nhq/btsDxS4rqJx/G0aI8HTHmbiW9DICiNkOzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co3nhq/btsDxS4rqJx/G0aI8HTHmbiW9DICiNkOzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco3nhq%2FbtsDxS4rqJx%2FG0aI8HTHmbiW9DICiNkOzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;709&quot; height=&quot;344&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 현재 퀄리티 상태의 Universal Render Pipeline Asset을 참조합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퀄리티 옵션이 변경되면 해당 에셋을 참조하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;URP RendererData List 참조&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1705402908157&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FieldInfo propertyInfo = pipelineAsset.GetType().GetField(&quot;m_RendererDataList&quot;, BindingFlags.Instance | BindingFlags.NonPublic);
ScriptableRendererData[] rendererDatas = (ScriptableRendererData[])propertyInfo.GetValue(pipelineAsset);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ad7gL/btsDuwVzRDU/4tCbBAQoZ4iqC9BcrAs1ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ad7gL/btsDuwVzRDU/4tCbBAQoZ4iqC9BcrAs1ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ad7gL/btsDuwVzRDU/4tCbBAQoZ4iqC9BcrAs1ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAd7gL%2FbtsDuwVzRDU%2F4tCbBAQoZ4iqC9BcrAs1ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;491&quot; height=&quot;223&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조한 Universal Render Pipeline Asset 내부의 Renderer List를 참조하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 내장 코드에서 Internal로 선언되어 직접 참조는 불가능하여 리플렉션 참조를 해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음, RendererList에서 원하는 인덱스를 참조합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rendererDatas[rendererIndex];&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Universal RendererData 내부 RendererFeatures 참조&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1705403259823&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;ScriptableRendererFeature&amp;gt; rendererFeatures = rendererData.rendererFeatures;
if (rendererFeatures == null || rendererFeatures.Count &amp;lt;= 0) return;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvMLeQ/btsDux1gYsK/k0tstZ0ycCzjFecfnGgkEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvMLeQ/btsDux1gYsK/k0tstZ0ycCzjFecfnGgkEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvMLeQ/btsDux1gYsK/k0tstZ0ycCzjFecfnGgkEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvMLeQ%2FbtsDux1gYsK%2Fk0tstZ0ycCzjFecfnGgkEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;310&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 통해 URP RendererData 내부의 RendererFeature 리스트를 참조할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZVFXT/btsDxU816Cp/rqhPgcTAFWFClOofEWumU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZVFXT/btsDxU816Cp/rqhPgcTAFWFClOofEWumU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZVFXT/btsDxU816Cp/rqhPgcTAFWFClOofEWumU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZVFXT%2FbtsDxU816Cp%2FrqhPgcTAFWFClOofEWumU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;544&quot; height=&quot;122&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플 코드에서는 제네릭 문법을 활용해 찾는 RendererFeature 클래스를 선택 할 수 있게 만들 었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 RendererFeature를 참조하면 SetActive 함수로 활성화 &amp;amp; 비활성화 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/makisin/items/5d66f1ea97f525f0c6d8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://qiita.com/makisin/items/5d66f1ea97f525f0c6d8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Unity/Programming</category>
      <category>RendererFeature Access</category>
      <category>RendererFeature Toggle</category>
      <category>unity</category>
      <category>URP</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/162</guid>
      <comments>https://darkcatgame.tistory.com/162#entry162comment</comments>
      <pubDate>Tue, 16 Jan 2024 20:10:13 +0900</pubDate>
    </item>
    <item>
      <title>Unity Linear ColorSpace에서 UI 알파가 다르게 보이는 이슈</title>
      <link>https://darkcatgame.tistory.com/161</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DL8nD/btsC00uocrf/Vx88HZUMKFK5dJmi9SBOTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DL8nD/btsC00uocrf/Vx88HZUMKFK5dJmi9SBOTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DL8nD/btsC00uocrf/Vx88HZUMKFK5dJmi9SBOTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDL8nD%2FbtsC00uocrf%2FVx88HZUMKFK5dJmi9SBOTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;704&quot; height=&quot;450&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;752&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 엔진에서 ColorSpace(색공간)을 Gamma, Linear 2가지 중 하나를 선택할 수 있으며 UI를 포함한 2D 텍스처 알파가 ColorSpace에 따라 다르게 보이는 이슈가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 이러한 이슈를 해결하기 위한 리서칭, 그리고 작업자를 위한 최적의 세팅 가이드를 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이슈 원인 - Linear ColorSpace에서 알파가 다르게 보이는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xQ4mB/btsCTYxDfnY/Ki1HGnRrp0IaQodu3gAci1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xQ4mB/btsCTYxDfnY/Ki1HGnRrp0IaQodu3gAci1/img.png&quot; data-alt=&quot;https://www.slideshare.net/jpcorp/ss-96115075&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xQ4mB/btsCTYxDfnY/Ki1HGnRrp0IaQodu3gAci1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxQ4mB%2FbtsCTYxDfnY%2FKi1HGnRrp0IaQodu3gAci1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;393&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.slideshare.net/jpcorp/ss-96115075&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 엔진에서 Linear ColorSpace를 사용하면 알파 블랜딩, 쉐이더 연산 값이 다르게 변합니다. 이는 PBR(물리기반렌더링) 3D 게임에서 더 현실적이고 정확한 조명 연산을 위해 필요한 기능입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 포토샵 등 텍스처 에셋 제작은 Gamma ColorSpace 기준으로 제작하기 때문에 포토샵에서 알파가 다르게 보이는 이슈가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용은 간단 요약한 것이며 ColorSpace에 대한 자세한 이론이 궁금하시다면 아래 자료를 참고해주시길 바랍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;감마가 어디감마(대마왕님) : &lt;a href=&quot;https://www.youtube.com/watch?v=Xwlm5V-bnBc&amp;amp;ab_channel=UnityKorea&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=Xwlm5V-bnBc&amp;amp;ab_channel=UnityKorea&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이슈 해결을 위한 리서칭&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linear ColorSpace에서 포토샵과 알파가 동일하게 보이기 위해 3가지 시도를 해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 3가지 방법을 소개하고, 결과 리뷰를 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. UI 쉐이더에서 알파 값 감마 보정&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kAfEq/btsCUYxoRGx/gDRtOFyNq8zifpRpatpSZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kAfEq/btsCUYxoRGx/gDRtOFyNq8zifpRpatpSZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kAfEq/btsCUYxoRGx/gDRtOFyNq8zifpRpatpSZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkAfEq%2FbtsCUYxoRGx%2FgDRtOFyNq8zifpRpatpSZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;236&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity Sprite 혹은 UGUI Image 컴포넌트에 커스텀 쉐이더 기반 Material을 세팅할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 세팅되는 커스텀 쉐이더에서 알파 값 감마 보정을 시도했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더 코드는 아래와 같습니다. fragment 쉐이더에서 알파 값에 대한 감마 보정 코드를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감마 보정은 pow(alpha, 2.2)해도 되고, 유니티 내장 함수에 있는 코드를 사용해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2개 방법 전부 밴딩 현상 문제 있음)&lt;/p&gt;
&lt;pre id=&quot;code_1704195606941&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; // Unity 내장 함수
 inline half GammaToLinearSpace (half sRGB)
 {
     // Approximate version from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
     return sRGB * (sRGB * (sRGB * 0.305306011h + 0.682171111h) + 0.012522878h);
     // Precise version, useful for debugging.
     //return half3(GammaToLinearSpaceExact(sRGB.r), GammaToLinearSpaceExact(sRGB.g), GammaToLinearSpaceExact(sRGB.b));
 }

 float4 frag(v2f IN) : SV_Target
 {
     float4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd);// * IN.color;
     #if !UNITY_COLORSPACE_GAMMA
         // Gamma to Linear
        //color.a = GammaToLinearSpace(color.a);     // Unity 내장 함수
        color.a *= pow(color.a, 2.2f);
     #endif
     color *= IN.color;

     #ifdef UNITY_UI_CLIP_RECT
     color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
     #endif

     #ifdef UNITY_UI_ALPHACLIP
         clip (color.a - 0.001);
     #endif

     return color;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. AssetPostprocessor를 활용해 알파 보정하여 텍스처 임포팅&lt;br /&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1704195991987&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SpriteAssetPostprocessor : AssetPostprocessor
{
    private void OnPostprocessTexture(Texture2D texture)
    {
        TextureImporter textureImporter = (TextureImporter)assetImporter;

        ModifySpriteAlphaGammaToLinear(textureImporter, texture);
    }

    private static void ModifySpriteAlphaGammaToLinear(TextureImporter textureImporter, Texture2D texture)
    {
        if (textureImporter.textureType != TextureImporterType.Sprite ||
            PlayerSettings.colorSpace != ColorSpace.Linear) return;
        if (texture.name.Contains(&quot;Linear&quot;) == false) return;

        Color[] pixels = texture.GetPixels();
        for (int i = 0; i &amp;lt; pixels.Length; i++)
        {
            Color pixel = pixels[i];
            // pixel.a = Mathf.Pow(pixel.a, 2.2f);
            pixel.a = GammaToLinearSpace(pixel.a);
            pixels[i] = pixel;
        }
        texture.SetPixels(pixels);
        texture.Apply();
        Debug.Log(texture.name);
    }

    static float GammaToLinearSpace(float sRGB)
    {
        // Approximate version from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
        return sRGB * (sRGB * (sRGB * 0.305306011f + 0.682171111f) +0.012522878f);

        // Precise version, useful for debugging.
        //return half3(GammaToLinearSpaceExact(sRGB.r), GammaToLinearSpaceExact(sRGB.g), GammaToLinearSpaceExact(sRGB.b));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C# 스크립트에서 Assetpostprocessor 클래스를 활용해 텍스처를 엔진에서 임포트 할때 자동으로 알파 값을 보정하는 기능을 개발했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더에서 처리하는 것보다 성능적으로 더 좋다고 판단해서 시도했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 포토샵에서 색상 공간 변경&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbeGk5/btsCTxAfLgd/PnzXsxFMHrnaNDB5CniSe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbeGk5/btsCTxAfLgd/PnzXsxFMHrnaNDB5CniSe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbeGk5/btsCTxAfLgd/PnzXsxFMHrnaNDB5CniSe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbeGk5%2FbtsCTxAfLgd%2FPnzXsxFMHrnaNDB5CniSe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;893&quot; height=&quot;571&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 엔진에서 아무것도 하지 않고 포토샵에서 색상 공간을 변경하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포토샵의 &lt;b&gt;&quot;색상 설정&quot;&lt;/b&gt; 옵션에서 &lt;b&gt;&quot;감마를 사용하여 RGB 색상 혼합&quot; 옵션을 활성화&lt;/b&gt;하고 리소스 제작 작업을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 옵션은 기본적으로 비활성화되어 있지만 활성화한다면 엔진에서 Linear ColorSpace 세팅한것과 동일한 룩으로 변경됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법의 단점은 포토샵 블랜딩 효과가 불량하게 동작할 수 있고 Gamma ColorSpace에서 다르게 보이는 이슈가 발생할 수 있으니 참고 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;u&gt;(24.04.25 비활성화 &amp;lt;-&amp;gt; 활성화 내용을 반대로 작성한 것 수정, 댓글 피드백 감사합니다)&lt;/u&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결과 리뷰&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d5YLxm/btsCX53RpmY/GsoQQLTGyt71JYvSFzJoB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d5YLxm/btsCX53RpmY/GsoQQLTGyt71JYvSFzJoB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d5YLxm/btsCX53RpmY/GsoQQLTGyt71JYvSFzJoB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd5YLxm%2FbtsCX53RpmY%2FGsoQQLTGyt71JYvSFzJoB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;984&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진은 엔진에서 감마, 리니어 결과 그리고 위 리서칭 3가지 방법에 대한 결과물을 모았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔진에서 처리하는 2가지 방법은 알파 강도가 비슷해지기는 했지만 색상 블랜딩이 여전히 다르게 보이는 이슈가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 포토샵에서 색상 설정 적용한 결과물은 엔진에서 Linear Colorspace와 동일한 결과를 보여주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;615&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qYGYr/btsCWJ02YAX/bXklDAefJViwpBElXE7n61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qYGYr/btsCWJ02YAX/bXklDAefJViwpBElXE7n61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qYGYr/btsCWJ02YAX/bXklDAefJViwpBElXE7n61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqYGYr%2FbtsCWJ02YAX%2FbXklDAefJViwpBElXE7n61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1205&quot; height=&quot;615&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;615&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 그라데이션 텍스처를 출력한 결과물입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔진에서 세팅한 2가지 방법은 색상 블랜딩 이슈도 있었지만, 위 사진을 자세히 보시면 밴딩 현상까지 보이는 품질 이슈가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Texture Importer(Asset Postprocessor)에서 처리한 결과물은 알파 값을 다르게 저장했기 때문에 알파 값 이슈도 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론 및 다른 시도 방향&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linear ColorSpace에서 알파가 다르게 보이는 이슈는 엔진에서 처리하면 문제가 해결되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적은 작업자가 포토샵에서 &quot;색상 설정&quot; 기능을 활용해 Linear ColorSpace에 맞는 리소스를 제작하는 것 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외 유니티 엔진에서 UGUI 렌더파이프라인에 대한 커스텀을 시도해볼 수 있을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>Linear ColorSpace 알파</category>
      <category>UGUI</category>
      <category>UI 알파</category>
      <category>unity</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/161</guid>
      <comments>https://darkcatgame.tistory.com/161#entry161comment</comments>
      <pubDate>Tue, 2 Jan 2024 21:01:31 +0900</pubDate>
    </item>
    <item>
      <title>URP 렌더피처를 활용한 Sprite 개별 블러 효과 구현</title>
      <link>https://darkcatgame.tistory.com/160</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;QEQWRQ.gif&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T0J7u/btsBqKHJUJu/79kj6NLX1y1majZ6kHtV4K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T0J7u/btsBqKHJUJu/79kj6NLX1y1majZ6kHtV4K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T0J7u/btsBqKHJUJu/79kj6NLX1y1majZ6kHtV4K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/T0J7u/btsBqKHJUJu/79kj6NLX1y1majZ6kHtV4K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;901&quot; height=&quot;443&quot; data-filename=&quot;QEQWRQ.gif&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpriteRenderer 오브젝트 개별로 블러 효과를 다르게 적용하는 것을 목표로 URP 렌더피처 및 쉐이더 개발 과정을 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개발 환경&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity 2021.3.30f1&lt;/li&gt;
&lt;li&gt;URP 12.1.12&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;핵심 요소&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다운 샘플링 블러 - 저비용 고품질 블러 효과&lt;/li&gt;
&lt;li&gt;레이어 필터 프리패스 블러 - 원하는 오브젝트만 필터링하여 블러 처리&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;GitHub 샘플 프로젝트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/LayerFilterSpriteBlur&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/CatDarkGame/LayerFilterSpriteBlur&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701780501543&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - CatDarkGame/LayerFilterSpriteBlur: Unity URP, Blur effect for each spriterenderer&quot; data-og-description=&quot;Unity URP, Blur effect for each spriterenderer. Contribute to CatDarkGame/LayerFilterSpriteBlur development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/CatDarkGame/LayerFilterSpriteBlur&quot; data-og-url=&quot;https://github.com/CatDarkGame/LayerFilterSpriteBlur&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/t7QYg/hyUFaVcQQQ/FBVUDDq9NZw34JJPv0yHv0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/CatDarkGame/LayerFilterSpriteBlur&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/CatDarkGame/LayerFilterSpriteBlur&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/t7QYg/hyUFaVcQQQ/FBVUDDq9NZw34JJPv0yHv0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - CatDarkGame/LayerFilterSpriteBlur: Unity URP, Blur effect for each spriterenderer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Unity URP, Blur effect for each spriterenderer. Contribute to CatDarkGame/LayerFilterSpriteBlur development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 1. 다운 샘플링 블러 - 저비용 고품질 블러 효과&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 목표는 모바일 환경까지 고려해 퍼포먼스 비용이 높지 않아야 하며, 블러 품질이 좋아야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 우선 블러를 표현하기 위한 여러가지 방법을 시도했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;가우시안 블러 + Mipmap 샘플링 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가우시안 블러는 주변 픽셀로 UV Offset 샘플링을 반복하여 블러를 표현하는 방식으로 가장 보편적으로 사용하는 블러 알고리즘입니다. 가우시안 블러는 만족스러운 블러 퀄리티를 표현하기 위해서는 샘플링 반복 횟수와 패스 스탭이 높여야합니다, 하지만 포스트프로세스가 아닌 SpriteRenderer에 블러 효과를 적용해야하기 때문에 이와 같은 방식을 그대로 적용하기는 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 1 Pass 가우시안 블러 9x9 방식에 Mipmap 샘플링하는 방식으로 블러를 표현해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701782313194&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; float GaussianWeight(int x, int y)
 {
     float sigma = 1.0;
     float norm = 1.0 / (2.0 * 3.141592 * sigma * sigma);
     float expPart = exp(-((x * x + y * y) / (2.0 * sigma * sigma)));
     return norm * expPart;
 }
 
 half4 frag (Varyings i) : SV_TARGET 
 {
     float2 uv = i.uv * _MainTex_ST.xy + _MainTex_ST.zw;
     half4 baseMap = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv); 
     half4 blurMap = baseMap;
     for (int x = -4; x &amp;lt;= 4; x++)
     {
         for (int y = -4; y &amp;lt;= 4; y++)
         {
             float weight = GaussianWeight(x, y);
             blurMap += SAMPLE_TEXTURE2D_LOD(_MainTex, sampler_MainTex, uv + (float2(x, y) * _Offset) * _MainTex_TexelSize, _Amount * 3) * weight;
         }
     }

     return lerp(baseMap, blurMap, _Amount);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20231116_225036248.gif&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R9mCM/btsBtICzsco/Zz54ZjU1w0mSg44bOIK1o1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R9mCM/btsBtICzsco/Zz54ZjU1w0mSg44bOIK1o1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R9mCM/btsBtICzsco/Zz54ZjU1w0mSg44bOIK1o1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/R9mCM/btsBtICzsco/Zz54ZjU1w0mSg44bOIK1o1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;873&quot; height=&quot;325&quot; data-filename=&quot;KakaoTalk_20231116_225036248.gif&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도한대로 블러가 유의미하게 표현되지만 목표한 블러 퀄리티에 비하면 부족하고 각 Sprite별로 82번 샘플링하는 것 자체가 GPU 성능 저하가 발생합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBqT02/btsBqurDlC8/EirnkV0l0B0NDgbFHOKAmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBqT02/btsBqurDlC8/EirnkV0l0B0NDgbFHOKAmk/img.png&quot; data-alt=&quot;목표 블러 퀄리티(포토샵 제작)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBqT02/btsBqurDlC8/EirnkV0l0B0NDgbFHOKAmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBqT02%2FbtsBqurDlC8%2FEirnkV0l0B0NDgbFHOKAmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;328&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;목표 블러 퀄리티(포토샵 제작)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;다른 블러 에셋 분석 - Translucent Image&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 에셋 스토어에서 판매하는 블러 에셋을 분석했습니다. 이 에셋은 모바일 WebGL에서도 구동 될 만큼 성능이 안정적이면서 블러 퀄리티도 매우 뛰어납니다. (아래 링크에서 WebGL 확인 가능)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UUfCF/btsBurtGtHR/oR4mJVWuYC1aI6PMmuNqdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UUfCF/btsBurtGtHR/oR4mJVWuYC1aI6PMmuNqdK/img.png&quot; data-alt=&quot;https://leloctai.com/asset/translucentimage/index.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UUfCF/btsBurtGtHR/oR4mJVWuYC1aI6PMmuNqdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUUfCF%2FbtsBurtGtHR%2FoR4mJVWuYC1aI6PMmuNqdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;339&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://leloctai.com/asset/translucentimage/index.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1642&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ACUus/btsBtJuK7vO/vJqhzcQ5o2Ph3lu7SjUQV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ACUus/btsBtJuK7vO/vJqhzcQ5o2Ph3lu7SjUQV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ACUus/btsBtJuK7vO/vJqhzcQ5o2Ph3lu7SjUQV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FACUus%2FbtsBtJuK7vO%2FvJqhzcQ5o2Ph3lu7SjUQV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;868&quot; height=&quot;471&quot; data-origin-width=&quot;1642&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에셋의 렌더링 구조는 위와 같습니다. 핵심 블러 알고리즘은 다운샘플링 + 스탭당 Box블러처리 방식으로 의외로 복잡한 알고리즘으로 구현되지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블러 처리 외에 커스텀 렌더피처 코드 구조가 잘 설계되어 있고 버퍼 업데이트 간격 조절 등 배울 만한 요소가 많은 에셋입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더 연산은 복잡하지 않고 대부분 다운 샘플링 처리에 의존하는 방식이기 때문에 안드로이드 기기(갤럭시s10e) 프로파일링 결과 GPU보다 CPU 비용이 더 높게 측정되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pgb3Z/btsBuxAqwMW/9d5UoWMbrcbjhuSRob31oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pgb3Z/btsBuxAqwMW/9d5UoWMbrcbjhuSRob31oK/img.png&quot; data-alt=&quot;1.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pgb3Z/btsBuxAqwMW/9d5UoWMbrcbjhuSRob31oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPgb3Z%2FbtsBuxAqwMW%2F9d5UoWMbrcbjhuSRob31oK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CPU 렌더쓰레드&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Gfx.PresentFrame (GPU Time)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Blur 활성화&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;1.8ms ~ 2.5ms&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;3.8ms ~ 4.3ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Blur 비활성화&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;0.9ms ~ 1.3ms&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;3.2ms ~ 3.9ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 2. 레이어 필터 프리패스 블러 - 원하는 오브젝트만 필터링하여 블러 처리 &lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;전략&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프리패스 버퍼에 특정 레이어 오브젝트 렌더링 (SpriteRenderer 1번 패스)&lt;/li&gt;
&lt;li&gt;프리패스 버퍼를 다운 샘플링 블러 처리 (위에서 소개한 블러 에셋 방식 적용)&lt;/li&gt;
&lt;li&gt;화면 버퍼에 SpriteRenderer 2번 패스 렌더링 &amp;amp; 블러 버퍼와 블랜딩 적용&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 1. 프리패스 버퍼에 특정 레이어 오브젝트 렌더링 (SpriteRenderer 1번 패스)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZZZZ1.gif&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XuokB/btsBqIJYbJm/nsuqx34ITOu1kHVInMOoa0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XuokB/btsBqIJYbJm/nsuqx34ITOu1kHVInMOoa0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XuokB/btsBqIJYbJm/nsuqx34ITOu1kHVInMOoa0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/XuokB/btsBqIJYbJm/nsuqx34ITOu1kHVInMOoa0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;901&quot; height=&quot;443&quot; data-filename=&quot;ZZZZ1.gif&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 SpriteRenderer에만 블러 효과를 적용하기 위해 먼저 프리패스 렌더패스를 제작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 버퍼에 렌더링하는 것이 아닌 Depthpass &amp;amp; ShadowPass와 같이 별도의 렌더 텍스처 버퍼에 렌더링하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701785206072&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class LayerFilterRendererPass_Prepass : ScriptableRenderPass
{
    private const string k_ProfilingSamplerName = &quot;LayerFilterPrepass&quot;;
    private const string k_RenderTextureName = &quot;_LayerFilterPrepassBufferRT&quot;;
    private const string k_TexturePropertyName = &quot;_LayerFilterPrepassBufferTex&quot;;

    public RenderTargetHandle prepassBufferRTH;
    private ProfilingSampler m_ProfilingSampler;

    private LayerMask _layerMask;
    private ShaderTagId _shaderTagId;

    private bool _isClearBlack = false;

    public LayerFilterRendererPass_Prepass(RenderPassEvent passEvent, LayerMask layerMask, ShaderTagId shaderTagId, bool isClearBlack)
    {
        renderPassEvent = passEvent;
        _layerMask = layerMask;
        _shaderTagId = shaderTagId;

        m_ProfilingSampler = new ProfilingSampler(k_ProfilingSamplerName);
        _isClearBlack = isClearBlack;
    }

    public void Setup(ref RenderTargetHandle source, RenderTargetIdentifier renderTargetDestination)
    {
        source.Init(k_RenderTextureName);
        prepassBufferRTH = source;
    }

    public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    {
        // Prepass 렌더링할 새로운 렌더버퍼 생성 (기존 colorpass에 렌더링하는게 아닌 별도 버퍼에 렌더링하기 위함)
        RenderTextureDescriptor renderTextureDescriptor = renderingData.cameraData.cameraTargetDescriptor;
        renderTextureDescriptor.graphicsFormat = GetGraphicsFormat();
        renderTextureDescriptor.msaaSamples = 1;
        cmd.GetTemporaryRT(prepassBufferRTH.id, renderTextureDescriptor);
        cmd.SetGlobalTexture(k_TexturePropertyName, prepassBufferRTH.Identifier());

        if(_isClearBlack) ConfigureClear(ClearFlag.All, Color.black);     // Pass 렌더링 이전에 프레임 Clear 세팅 (검은색 프레임에 Sprite 렌더링)
        ConfigureInput(ScriptableRenderPassInput.Color);
        ConfigureTarget(new RenderTargetIdentifier(prepassBufferRTH.Identifier(), 0, CubemapFace.Unknown, -1));     // Pass 렌더링 대상 세팅
    }

    public override void OnCameraCleanup(CommandBuffer cmd)
    {
        cmd.ReleaseTemporaryRT(prepassBufferRTH.id);
    }


    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        CommandBuffer cmd = CommandBufferPool.Get();
        using (new UnityEngine.Rendering.ProfilingScope(cmd, m_ProfilingSampler))
        {
            context.ExecuteCommandBuffer(cmd);
            cmd.Clear();

            DrawingSettings drawSetting = CreateDrawingSettings(_shaderTagId, ref renderingData, SortingCriteria.CommonTransparent);
            FilteringSettings filterSetting = new FilteringSettings(RenderQueueRange.transparent, _layerMask);
            context.DrawRenderers(renderingData.cullResults, ref drawSetting, ref filterSetting);
        }

        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

    public static GraphicsFormat GetGraphicsFormat()
    {
        if (SystemInfo.IsFormatSupported(GraphicsFormat.B10G11R11_UFloatPack32, FormatUsage.Linear | FormatUsage.Render))
        {
            return GraphicsFormat.B10G11R11_UFloatPack32;
        }
        else
        {
            return QualitySettings.activeColorSpace == ColorSpace.Linear
                ? GraphicsFormat.R8G8B8A8_SRGB
                : GraphicsFormat.R8G8B8A8_UNorm;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 사용되는 쉐이더는 2 Pass로 제작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 패스는 Prepass 렌더링, 2번 패스는 최종 화면 버퍼에 렌더링 &amp;amp; 블러 버퍼와 블랜딩하는 기능을 구현합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701785488116&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1번 패스 
half4 PrePassFragment (Varyings i) : SV_TARGET 
{
    float2 uv = i.uv * _MainTex_ST.xy + _MainTex_ST.zw;
    half4 baseMap = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv); 
    return baseMap;
}

// 2번 패스
half4 DrawPassFragment (Varyings i) : SV_TARGET 
{
    float2 uv = i.uv * _MainTex_ST.xy + _MainTex_ST.zw;
    half blendAmount = i.color.a * _BlendAmount;
    blendAmount = saturate(pow(blendAmount, 2.2));

    //half4 baseMap = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv); 
    half4 baseMap = SAMPLE_TEXTURE2D_LOD(_MainTex, sampler_MainTex, uv, blendAmount * 3.2); // 자연스러운 블랜딩을 위해 mipmap 샘플링 이용
    
    float2 uv_prepass = i.screenPosition.xy / i.screenPosition.w;
    half4 prepassMap = SAMPLE_TEXTURE2D(_LayerFilterCopypassBufferTex, sampler_LayerFilterCopypassBufferTex, uv_prepass); 
    
    half4 finalColor = lerp(baseMap, prepassMap, blendAmount);
    finalColor.a = baseMap.a;
    return finalColor;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 2. 프리패스 버퍼를 다운 샘플링 블러 처리 (위에서 소개한 블러 에셋 방식 적용)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZZZZ2.gif&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGHRhB/btsBvUvewtn/t7JOZ6PPhkalBtBCRn5nO0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGHRhB/btsBvUvewtn/t7JOZ6PPhkalBtBCRn5nO0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGHRhB/btsBvUvewtn/t7JOZ6PPhkalBtBCRn5nO0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bGHRhB/btsBvUvewtn/t7JOZ6PPhkalBtBCRn5nO0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;901&quot; height=&quot;443&quot; data-filename=&quot;ZZZZ2.gif&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 다운 샘플링 렌더패스입니다. 다운 샘플링 + 4방향 박스 블러 처리를 수행하는데 블러 퀄리티가 훌륭하게 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더 패스 코드에서는 이전 프리패스에서 생성한 렌더 텍스처를 참조해 다운 샘플링 처리합니다. &lt;br /&gt;(GetTemporaryRT 함수로 추가 렌더텍스처 생성하지 않음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701785689126&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class LayerFilterRendererPass_Copy : ScriptableRenderPass
{
    private const string k_ProfilingSamplerName = &quot;LayerFilter_Copypass&quot;;
    private const string k_TexturePropertyName = &quot;_LayerFilterCopypassBufferTex&quot;;
    private static int k_DownSampleTexPropertyName = Shader.PropertyToID(&quot;_DownsampleTex&quot;);
    private static int k_BlurOffsetPropertyName = Shader.PropertyToID(&quot;_blurOffset&quot;);
    private static int k_TexturePropertyID =&amp;gt; Shader.PropertyToID(k_TexturePropertyName);

    private RenderTargetHandle copypassBufferRTH;
    private ProfilingSampler m_ProfilingSampler;
    private Material _material;
    private Shader _shader;

    private int _blurIteration = 3;
    private float _blurOffset = 1.0f;

    public LayerFilterRendererPass_Copy(RenderPassEvent passEvent, Shader shader)
    {
        renderPassEvent = passEvent;
        _shader = shader;

        m_ProfilingSampler = new ProfilingSampler(k_ProfilingSamplerName);
    }


    public void Setup(ref RenderTargetHandle source, int blurIteration = 3, float blurOffset = 1.0f)
    {
        copypassBufferRTH = source;
        _blurIteration = blurIteration;
        _blurOffset = blurOffset;

        if(!_material &amp;amp;&amp;amp; _shader)
        {
            _material = CoreUtils.CreateEngineMaterial(_shader);
        }

        ConfigureClear(ClearFlag.None, Color.white);
        ConfigureTarget(copypassBufferRTH.id);
    }

    public void Destroy()
    {
        if (_material)
        {
            CoreUtils.Destroy(_material);
            _material = null;
        }
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (!_material || copypassBufferRTH == null) return;

        CommandBuffer cmd = CommandBufferPool.Get();
        using (new UnityEngine.Rendering.ProfilingScope(cmd, m_ProfilingSampler))
        {
            context.ExecuteCommandBuffer(cmd);
            cmd.Clear();

            int iteration = _blurIteration;
            int stepCount = Mathf.Max(iteration * 2 - 1, 1);
            string[] shaderIDStr = new string[stepCount];
            int[] shaderID = new int[stepCount];

            RenderTargetIdentifier identifier = new RenderTargetIdentifier(copypassBufferRTH.id);
            RenderTextureDescriptor rtdTempRT = renderingData.cameraData.cameraTargetDescriptor;
            rtdTempRT.msaaSamples = 1;

            int sourceSize_Width = rtdTempRT.width;
            int sourceSize_Height = rtdTempRT.height;

            // 다운 샘플링 Blit 반복
            for (int i = 0; i &amp;lt; stepCount; i++)
            {
                int downsampleIndex = SimplePingPong(i, iteration - 1);
                rtdTempRT.width = sourceSize_Width &amp;gt;&amp;gt; downsampleIndex + 1;
                rtdTempRT.height = sourceSize_Height &amp;gt;&amp;gt; downsampleIndex + 1;
                shaderIDStr[i] = k_TexturePropertyName + i.ToString();
                shaderID[i] = Shader.PropertyToID(shaderIDStr[i]);

                cmd.SetGlobalTexture(k_DownSampleTexPropertyName, identifier);
                _material.SetFloat(k_BlurOffsetPropertyName, _blurOffset);

                cmd.GetTemporaryRT(shaderID[i], rtdTempRT, FilterMode.Bilinear);
                cmd.Blit(identifier, new RenderTargetIdentifier(shaderIDStr[i]), _material, 0);
                if (i &amp;lt; stepCount - 1) identifier = new RenderTargetIdentifier(shaderIDStr[i]);
            }

            // 최종 Blit
            rtdTempRT.width = sourceSize_Width;
            rtdTempRT.height = sourceSize_Height;
            identifier = new RenderTargetIdentifier(k_TexturePropertyName);
            cmd.GetTemporaryRT(k_TexturePropertyID, rtdTempRT, FilterMode.Bilinear);
            cmd.Blit(identifier, new RenderTargetIdentifier(k_TexturePropertyName), _material, 0);

            // RT 메모리 해제
            for (int i = 0; i &amp;lt; stepCount; i++)
            {
                cmd.ReleaseTemporaryRT(shaderID[i]);
            }
            cmd.ReleaseTemporaryRT(k_TexturePropertyID);
        }

        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

    private static int SimplePingPong(int t, int max)
    {
        if (t &amp;gt; max) return 2 * max - t;
        return t;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blit에서 활용하는 쉐이더는 단순하게 4방향 박스 블러 샘플링처리만 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701785835092&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float4 frag(Varyings input) : SV_Target
{
    float2 baseMapUV = input.uv.xy;

    float offset = _blurOffset;
    float4 tex_1 = SAMPLE_TEXTURE2D(_DownsampleTex, sampler_linear_clamp, baseMapUV + float2(_DownsampleTex_TexelSize.x * -offset, 0.0));
    float4 tex_2 = SAMPLE_TEXTURE2D(_DownsampleTex, sampler_linear_clamp, baseMapUV + float2(_DownsampleTex_TexelSize.x * offset, 0.0));
    float4 tex_3 = SAMPLE_TEXTURE2D(_DownsampleTex, sampler_linear_clamp, baseMapUV + float2(0.0, _DownsampleTex_TexelSize.y * -offset));
    float4 tex_4 = SAMPLE_TEXTURE2D(_DownsampleTex, sampler_linear_clamp, baseMapUV + float2(0.0, _DownsampleTex_TexelSize.y * offset));
    
    float4 finalColor = (tex_1 + tex_2 + tex_3 + tex_4) * 0.25f;
    return finalColor;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 3. 화면 버퍼에 SpriteRenderer 2번 패스 렌더링 &amp;amp; 블러 버퍼와 블랜딩 적용&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ZZZZ3.gif&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MaRde/btsBrfHArjq/r1IY4SrVnkkreVgqejZNx1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MaRde/btsBrfHArjq/r1IY4SrVnkkreVgqejZNx1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MaRde/btsBrfHArjq/r1IY4SrVnkkreVgqejZNx1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/MaRde/btsBrfHArjq/r1IY4SrVnkkreVgqejZNx1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;901&quot; height=&quot;443&quot; data-filename=&quot;ZZZZ3.gif&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 화면 버퍼에 렌더링하는 렌더 패스입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 렌더 패스에서는 단순히 SpriteRenderer 쉐이더의 2번 패스를 렌더링하는 역할만 수행하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더에서 이전 블러 패스에서 제작된 블러 텍스처와 Sprite 텍스처를 단순히 Lerp 연산으로 블랜딩하여 블러 강도를 조절합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 블러 텍스처와 자연스러운 블랜딩을 위해 mipmap 샘플링까지 추가했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701786192476&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class LayerFilterRendererPass_Drawpass : ScriptableRenderPass
{
    private const string k_ProfilingSamplerName = &quot;LayerFilterDrawpass&quot;;

    private ProfilingSampler m_ProfilingSampler;

    private LayerMask _layerMask;
    private ShaderTagId _shaderTagId;

    public LayerFilterRendererPass_Drawpass(RenderPassEvent passEvent, LayerMask layerMask, ShaderTagId shaderTagId)
    {
        renderPassEvent = passEvent;
        _layerMask = layerMask;
        _shaderTagId = shaderTagId;

        m_ProfilingSampler = new ProfilingSampler(k_ProfilingSamplerName);
    }

    public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    {
        ConfigureInput(ScriptableRenderPassInput.None);     // 필요한 렌더버퍼 명시 함수, Copypass에서 원본 패스로 전환하기 위해 사용
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        CommandBuffer cmd = CommandBufferPool.Get();
        using (new UnityEngine.Rendering.ProfilingScope(cmd, m_ProfilingSampler))
        {
            context.ExecuteCommandBuffer(cmd);
            cmd.Clear();

            DrawingSettings drawSettings = CreateDrawingSettings(_shaderTagId, ref renderingData, SortingCriteria.CommonTransparent);
            FilteringSettings filterSetting = new FilteringSettings(RenderQueueRange.transparent, _layerMask);
            context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSetting);
        }

        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701786230471&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 2번 패스
half4 DrawPassFragment (Varyings i) : SV_TARGET 
{
    float2 uv = i.uv * _MainTex_ST.xy + _MainTex_ST.zw;
    half blendAmount = i.color.a * _BlendAmount;
    blendAmount = saturate(pow(blendAmount, 2.2));

    //half4 baseMap = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv); 
    half4 baseMap = SAMPLE_TEXTURE2D_LOD(_MainTex, sampler_MainTex, uv, blendAmount * 3.2); // 자연스러운 블랜딩을 위해 mipmap 샘플링 이용
    
    float2 uv_prepass = i.screenPosition.xy / i.screenPosition.w;
    half4 prepassMap = SAMPLE_TEXTURE2D(_LayerFilterCopypassBufferTex, sampler_LayerFilterCopypassBufferTex, uv_prepass); 
    
    half4 finalColor = lerp(baseMap, prepassMap, blendAmount);
    finalColor.a = baseMap.a;
    return finalColor;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;한계점&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Dynamic Batching을 위해 버텍스 컬러 알파 값으로 블러 강도 조절&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpriteRenderer의 색상 값 외에 다른 Material Property를 추가하면 Dynamic Batching이 깨지는 이슈가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 포스팅에서 소개하는 예제에서는 알파 색상 값으로 블러 강도를 조절하게 제작했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관련 포스팅 : &lt;a href=&quot;https://darkcatgame.tistory.com/159&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://darkcatgame.tistory.com/159&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFfDe4/btsBuhSdBLQ/ZZvIMLKZvvoN7zJza3EKgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFfDe4/btsBuhSdBLQ/ZZvIMLKZvvoN7zJza3EKgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFfDe4/btsBuhSdBLQ/ZZvIMLKZvvoN7zJza3EKgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFfDe4%2FbtsBuhSdBLQ%2FZZvIMLKZvvoN7zJza3EKgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1466&quot; height=&quot;700&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;블러 강도 중간 단계가 어색함&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 완성된 블러 텍스처와 Lerp 블랜딩하는 방식이기 때문에 블러 강도가 중간 값이면 블러가 퍼지는 느낌이 부족합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mipmap 샘플링을 최대한 보완했지만 여전히 어색합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ylOrw/btsBuaZ232A/FPdmJ3kvy0T1n4UZ9DMwG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ylOrw/btsBuaZ232A/FPdmJ3kvy0T1n4UZ9DMwG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ylOrw/btsBuaZ232A/FPdmJ3kvy0T1n4UZ9DMwG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FylOrw%2FbtsBuaZ232A%2FFPdmJ3kvy0T1n4UZ9DMwG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;449&quot; height=&quot;423&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스프라이트 외부 영역 블러 침범 &amp;amp; 알파 마스킹 영역에 블러까지 끊김&lt;br /&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sprite 텍스처의 알파 마스킹 영역(투명 픽셀)이 존재하는 경우 블러가 끊겨 보이는 문제와 스프라이트 외부 픽셀이 침범하는 문제가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알파 정보가 없는 블러 패스를 샘플링하는 방식이기 때문에 이런 이슈가 발생합니다.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ry4Hu/btsBvoJ0VO6/tyZFlBlbvVxF69E5mbgNu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ry4Hu/btsBvoJ0VO6/tyZFlBlbvVxF69E5mbgNu1/img.png&quot; data-alt=&quot;1.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ry4Hu/btsBvoJ0VO6/tyZFlBlbvVxF69E5mbgNu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRy4Hu%2FbtsBvoJ0VO6%2FtyZFlBlbvVxF69E5mbgNu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prepass에 렌더링 할 때 뒷 배경을 포함하는 방식으로 어느 정도 완화 할 수 있지만 SpriteRenderer는 알파 모양에 맞게 메쉬를 재구성하는 특성 때문에 완벽하게 해결하기는 어렵습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFcmzC/btsBojD2ohm/KBgx5DQRSnLzki7Ljcfco0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFcmzC/btsBojD2ohm/KBgx5DQRSnLzki7Ljcfco0/img.png&quot; data-origin-width=&quot;269&quot; data-origin-height=&quot;268&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.4284%; margin-right: 10px;&quot; data-widthpercent=&quot;49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFcmzC/btsBojD2ohm/KBgx5DQRSnLzki7Ljcfco0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFcmzC%2FbtsBojD2ohm%2FKBgx5DQRSnLzki7Ljcfco0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;269&quot; height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs6XYn/btsBtHRcPic/0xRxAZ0DkCC9UlHTgwC24K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs6XYn/btsBtHRcPic/0xRxAZ0DkCC9UlHTgwC24K/img.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;268&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.4088%;&quot; data-widthpercent=&quot;51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs6XYn/btsBtHRcPic/0xRxAZ0DkCC9UlHTgwC24K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs6XYn%2FbtsBtHRcPic%2F0xRxAZ0DkCC9UlHTgwC24K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : 개선 전 우 : 개선 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prepass에 뒷 배경 포함하는 기능은 렌더피처의 Use Copy Color Pass 옵션을 활성화하면 되고 코드는 LayerFilterRendererPass_CopyColor 클래스를 참고해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x0LBs/btsBtI3FhSA/HPheySCDMmW7DuWRtCTOd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x0LBs/btsBtI3FhSA/HPheySCDMmW7DuWRtCTOd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x0LBs/btsBtI3FhSA/HPheySCDMmW7DuWRtCTOd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx0LBs%2FbtsBtI3FhSA%2FHPheySCDMmW7DuWRtCTOd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;320&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;notranslate&quot; style=&quot;all: initial;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>Blur</category>
      <category>Sprite Blur</category>
      <category>Sprite 개별 블러</category>
      <category>unity</category>
      <category>URP</category>
      <category>다운 샘플링 블러</category>
      <category>렌더패스</category>
      <category>블러</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/160</guid>
      <comments>https://darkcatgame.tistory.com/160#entry160comment</comments>
      <pubDate>Tue, 5 Dec 2023 23:41:58 +0900</pubDate>
    </item>
    <item>
      <title>SpriteRenderer CustomVertex Attributes R&amp;amp;D</title>
      <link>https://darkcatgame.tistory.com/159</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity SpriteRenderer에 커스텀 쉐이더를 사용할 수 있지만 MaterialPropertyBlock을 사용하는 경우 Dynamic Batcing이 묶이지 않기 때문에 사용이 제한됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Particle System의 CustomVertexSteam, UGUI의 BaseMeshEffect 클래스와 같이 Vertex Attributes를 추가하는 방식으로 커스텀 쉐이더를 사용해도 배칭 이슈가 발생하지 않는데 SpriteRenderer는 기본 Vertex Color 외에 다른 방법이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 SpriteRenderer에 Vertex Attributes를 추가하는 방법에 대한 R&amp;amp;D 내용을 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Sprite.SetVertexAttribute 활용&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1701692442775&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void ModifyVertexAttribute(SpriteRenderer spriteRenderer)
{
    Sprite sprite = spriteRenderer.sprite;
    if(sprite == null) return;
    
    int vertexCount = sprite.GetVertexCount();
    NativeArray&amp;lt;Vector2&amp;gt; texcoords2 = new NativeArray&amp;lt;Vector2&amp;gt;(vertexCount, Allocator.Temp);

    for (int i = 0; i &amp;lt; vertexCount; i++)
    {
        texcoords2[i] = new Vector2(_blurPower, _BlendAmount);
    }

    sprite.SetVertexAttribute(VertexAttribute.TexCoord1, texcoords2);
    texcoords2.Dispose();

    spriteRenderer.sprite = sprite;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Sprite.SetVertexAttribute 함수를 통해 Sprite의 버텍스 정보를 수정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 Texcoord1 항목을 추가했으며 쉐이더에서는 TEXCOORD1 값을 받아 Fragment로 전달하여 값을 활용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Fz7W/btsBqZKrIMu/pBjvOVMbONAcmeoKsKcvG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Fz7W/btsBqZKrIMu/pBjvOVMbONAcmeoKsKcvG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Fz7W/btsBqZKrIMu/pBjvOVMbONAcmeoKsKcvG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Fz7W%2FbtsBqZKrIMu%2FpBjvOVMbONAcmeoKsKcvG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;457&quot; height=&quot;355&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;355&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과, 의도한대로 Vertex Attribute로 값을 전달하여 작동까지 되며, 배칭이 유지되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;qweqzzzzds(1).gif&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n27yP/btsBq3TBS9v/NfEfkK6lDaSSm5OiyOjR7k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n27yP/btsBq3TBS9v/NfEfkK6lDaSSm5OiyOjR7k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n27yP/btsBq3TBS9v/NfEfkK6lDaSSm5OiyOjR7k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/n27yP/btsBq3TBS9v/NfEfkK6lDaSSm5OiyOjR7k/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;256&quot; data-filename=&quot;qweqzzzzds(1).gif&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;한계점 - Sprite 에셋을 새로 생성 필요&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JyQUm/btsBmTYKhHy/81kUuBAT8Om9rI2xRG4T5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JyQUm/btsBmTYKhHy/81kUuBAT8Om9rI2xRG4T5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JyQUm/btsBmTYKhHy/81kUuBAT8Om9rI2xRG4T5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJyQUm%2FbtsBmTYKhHy%2F81kUuBAT8Om9rI2xRG4T5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;388&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity Sprite의 내부 구조를 확인할 수 없지만, Sprite 에셋 자체가 Mesh처럼 버텍스 정보를 포함하고 있는 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 방식이 동작하기 위해서는 VertexAttribute를 수정하는 Sprite마다 에셋을 새로 생성해야 하며 메모리 부담이 높아지는 위험성이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅에서 소개한 VertexAttribute 방식은 메모리 이슈로 사용하면 안됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 SpriteRenderer 시스템을 구현하는 방법이 가장 적절할 것으로 판단합니다. (맨 아래 레퍼런스 참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*Unity 2023.1부터 SpriteRenderer에 SRPBatcher가 지원됩니다. (23.12.04기준 아직 Beta버전)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 전체 코드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SpriteRenderer에 붙이는 스크립트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701693125566&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;
using UnityEngine.U2D;
using UnityEngine.Rendering;
using Unity.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif


namespace CatDarkGame
{
    //[ExecuteInEditMode]
    [RequireComponent(typeof(SpriteRenderer))]
    [DisallowMultipleComponent]
    public class SpriteBlurRenderer : MonoBehaviour
    {
        [SerializeField][HideInInspector] private Material _materialAsset = null;
        [SerializeField][HideInInspector] private SpriteRenderer _spriteRenderer;
        private Sprite _sprite;
        
        [Range(0.01f, 16)][SerializeField] private float _blurPower = 2.0f;
        [Range(0, 1)][SerializeField] private float _BlendAmount = 0.5f;
        [SerializeField] private bool _customVertexStream = false;

        private void OnEnable()
        {
#if UNITY_EDITOR
            if (!_materialAsset)
            {
                string assetPath = GetScriptAssetPath + &quot;/Material/CatDarkGame_Sprites_Sprite_Blur.mat&quot;;
                Debug.Log(assetPath);
                _materialAsset = AssetDatabase.LoadAssetAtPath&amp;lt;Material&amp;gt;(assetPath);
            }
#endif
            if (!_spriteRenderer) _spriteRenderer = GetComponent&amp;lt;SpriteRenderer&amp;gt;();
            _spriteRenderer.material = _materialAsset;


            // Sprite 에셋을 새로 생성하는 방식, (메모리 증가 위험)
            // Unity Sprite 시스템은 Sprite 에셋 = Mesh 역할.
            // 결론 : 이 방식도 올바른 솔루션이 아님
            if (_spriteRenderer &amp;amp;&amp;amp; !_sprite)
            {
                _sprite = Instantiate(_spriteRenderer.sprite);
                _sprite.hideFlags = HideFlags.HideAndDontSave;
                _sprite.name = _sprite.name.Replace(&quot;(Clone)&quot;, &quot;&quot;);
                _spriteRenderer.sprite = _sprite;
            }

        }

        private bool CheckCanUpdate()
        {
            if (!_spriteRenderer) return false;
            return true;
        }

        private void Update()
        {
            if (!CheckCanUpdate()) return;
            
            if (!_customVertexStream)
            {
                _materialAsset.DisableKeyword(&quot;_CUSTOMVERTEXSTREAM&quot;);
                MaterialPropertyBlock mpb = new MaterialPropertyBlock();
                _spriteRenderer.GetPropertyBlock(mpb);
                mpb.SetFloat(&quot;_BlurPower&quot;, _blurPower);
                mpb.SetFloat(&quot;_BlendAmount&quot;, _BlendAmount);
                _spriteRenderer.SetPropertyBlock(mpb);
            }
            else
            {
                _materialAsset.EnableKeyword(&quot;_CUSTOMVERTEXSTREAM&quot;);
                ModifyVertexAttribute(_spriteRenderer);
            }
        }

        void ModifyVertexAttribute(SpriteRenderer spriteRenderer)
        {
            Sprite sprite = spriteRenderer.sprite;
            if(sprite == null) return;
            
            int vertexCount = sprite.GetVertexCount();
            NativeArray&amp;lt;Vector2&amp;gt; texcoords2 = new NativeArray&amp;lt;Vector2&amp;gt;(vertexCount, Allocator.Temp);

            for (int i = 0; i &amp;lt; vertexCount; i++)
            {
                texcoords2[i] = new Vector2(_blurPower, _BlendAmount);
            }

            sprite.SetVertexAttribute(VertexAttribute.TexCoord1, texcoords2);
            texcoords2.Dispose();

            spriteRenderer.sprite = sprite;
        }


#if UNITY_EDITOR
        private string GetScriptAssetPath
        {
            get
            {
                string path = AssetDatabase.FindAssets($&quot;t:Script {nameof(SpriteBlurRenderer)}&quot;)[0];
                path = AssetDatabase.GUIDToAssetPath(path);
                return path.Substring(0, path.LastIndexOf(&quot;/&quot;));
            }
        }
#endif
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;쉐이더&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701693155513&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shader &quot;CatDarkGame/Sprites/Sprite_Blur&quot;
{
    /* 
       
    */
    Properties
    {
        //[PerRendererData] 
        _MainTex (&quot;Sprite Texture&quot;, 2D) = &quot;white&quot; {}

        _BlurPower(&quot;Blur Power&quot;, Range(0.01, 16)) = 2
        _BlendAmount(&quot;Blend Amount&quot;, Range(0,1)) = 0.5

         [Toggle(_CUSTOMVERTEXSTREAM)]_EnableCustomVertexStream(&quot;Enable CustomVertexStream&quot;, float) = 0

        // Legacy properties. They're here so that materials using this shader can gracefully fallback to the legacy sprite shader.
        [HideInInspector] _Color (&quot;Tint&quot;, Color) = (1,1,1,1)
        [HideInInspector] PixelSnap (&quot;Pixel snap&quot;, Float) = 0
        [HideInInspector] _RendererColor (&quot;RendererColor&quot;, Color) = (1,1,1,1)
        [HideInInspector] _Flip (&quot;Flip&quot;, Vector) = (1,1,1,1)
        [HideInInspector] _AlphaTex (&quot;External Alpha&quot;, 2D) = &quot;white&quot; {}
        [HideInInspector] _EnableExternalAlpha (&quot;Enable External Alpha&quot;, Float) = 0
    }

    SubShader
    {
        Tags {&quot;Queue&quot; = &quot;Transparent&quot; &quot;RenderType&quot; = &quot;Transparent&quot; &quot;RenderPipeline&quot; = &quot;UniversalPipeline&quot; }

        Blend SrcAlpha OneMinusSrcAlpha
        Cull Off
        ZWrite Off


        Pass
        {
            //Tags { &quot;LightMode&quot; = &quot;Universal2D&quot; }
            Tags { &quot;LightMode&quot; = &quot;UniversalForward&quot; &quot;Queue&quot;=&quot;Transparent&quot; &quot;RenderType&quot;=&quot;Transparent&quot;}

            HLSLPROGRAM
            #pragma target 4.5
            #pragma multi_compile_instancing
            #pragma multi_compile_fragment _ DEBUG_DISPLAY

            #pragma shader_feature _CUSTOMVERTEXSTREAM

            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;
            #include &quot;Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl&quot;
            #include &quot;HLSL/CommonUtil.hlsl&quot;
            #if defined(DEBUG_DISPLAY)
                #include &quot;Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/InputData2D.hlsl&quot;
                #include &quot;Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/SurfaceData2D.hlsl&quot;
                #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Debug/Debugging2D.hlsl&quot;
            #endif

            #pragma vertex UnlitVertex
            #pragma fragment UnlitFragment

            struct Attributes
            {
                float3 positionOS   : POSITION;
                float4 color        : COLOR;
                float2 uv           : TEXCOORD0;
                float2 custom           : TEXCOORD1;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct Varyings
            {
                float4  positionCS      : SV_POSITION;
                float4  color           : COLOR;
                float2  uv              : TEXCOORD0;
                float2 custom           : TEXCOORD1;
                #if defined(DEBUG_DISPLAY)
                    float3  positionWS      : TEXCOORD2;
                #endif
                UNITY_VERTEX_OUTPUT_STEREO
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
            float4 _MainTex_ST;
           
            float4 _Color;
            half4 _RendererColor;
           
           
            #ifdef UNITY_INSTANCING_ENABLED
                UNITY_INSTANCING_BUFFER_START(Props)
                    UNITY_DEFINE_INSTANCED_PROP(half, _BlurPower)
                    UNITY_DEFINE_INSTANCED_PROP(half, _BlendAmount)

                UNITY_INSTANCING_BUFFER_END(Props)

                #define _BlurPower      UNITY_ACCESS_INSTANCED_PROP(Props, _BlurPower)
                #define _BlendAmount    UNITY_ACCESS_INSTANCED_PROP(Props, _BlendAmount)
            #endif 

           CBUFFER_START(UnityPerDrawSprite)
            #ifndef UNITY_INSTANCING_ENABLED
                half _BlurPower;
                half _BlendAmount;
            #endif
            CBUFFER_END

            Varyings UnlitVertex(Attributes attributes)
            {
                Varyings o = (Varyings)0;
                UNITY_SETUP_INSTANCE_ID(attributes);
                UNITY_TRANSFER_INSTANCE_ID(attributes, o);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

                o.positionCS = TransformObjectToHClip(attributes.positionOS);
                #if defined(DEBUG_DISPLAY)
                    o.positionWS = TransformObjectToWorld(attributes.positionOS);
                #endif
                o.uv = TRANSFORM_TEX(attributes.uv, _MainTex);
                o.color = attributes.color * _Color * _RendererColor;

                o.custom = attributes.custom;
                return o;
            }


            float4 UnlitFragment(Varyings i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);
               
                float2 uv = i.uv;
                float4 mainTex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
                
                float3 blurCol = 0;
                float blurAlpha = 0;
                half blurPower = _BlurPower;
                #ifdef _CUSTOMVERTEXSTREAM
                    blurPower = i.custom.x;
                #endif
                SumBlur_float(TEXTURE2D_ARGS(_MainTex, sampler_MainTex), uv, blurPower, blurCol, blurAlpha);

                float4 result = 1;
                half blendAmount = _BlendAmount;
                #ifdef _CUSTOMVERTEXSTREAM
                    blendAmount = i.custom.y;
                #endif
                half smoothAmount = smoothstep(0.0, 1, blendAmount); // _BlendAmount;
                result = lerp(mainTex, float4(blurCol, blurAlpha), smoothAmount) * i.color;

                
                #if defined(DEBUG_DISPLAY)
                    SurfaceData2D surfaceData;
                    InputData2D inputData;
                    half4 debugColor = 0;

                    InitializeSurfaceData(result.rgb, result.a, surfaceData);
                    InitializeInputData(i.uv, inputData);
                    SETUP_DEBUG_DATA_2D(inputData, i.positionWS);

                    if(CanDebugOverrideOutputColor(surfaceData, inputData, debugColor))
                    {
                        return debugColor;
                    }
                #endif

                return result;
            }
            ENDHLSL
        }
    }


    Fallback &quot;Sprites/Default&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;레퍼런스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 포럼 : Additional&amp;nbsp;Per-Vertex&amp;nbsp;Data&amp;nbsp;for&amp;nbsp;SpriteRenderer?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://forum.unity.com/threads/additional-per-vertex-data-for-spriterenderer.1270244/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://forum.unity.com/threads/additional-per-vertex-data-for-spriterenderer.1270244/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECS Instanced SpriteRenderer github&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/paullj/unity-ecs-instanced-sprite-renderer/blob/master/Assets/Scripts/Rendering/SpriteInstanceRenderSystem.cs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/paullj/unity-ecs-instanced-sprite-renderer/blob/master/Assets/Scripts/Rendering/SpriteInstanceRenderSystem.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entitiy 시스템으로 SpriteRenderer 구현&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tsyang.tistory.com/161&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tsyang.tistory.com/161&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity Sprite GPU Instance github&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ownself/UnitySpriteGPUInstancing&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/ownself/UnitySpriteGPUInstancing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/159</guid>
      <comments>https://darkcatgame.tistory.com/159#entry159comment</comments>
      <pubDate>Mon, 4 Dec 2023 21:37:49 +0900</pubDate>
    </item>
    <item>
      <title>Shader Keyword &amp;amp; Variants 체크 툴</title>
      <link>https://darkcatgame.tistory.com/158</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트에서 사용하는 특정 쉐이더의 키워드 및 Variants 숫자를 리스트로 정렬해주는 툴&lt;/li&gt;
&lt;li&gt;사용되는 키워드 분량, 필요 없는 키워드를 판별하는 용도로 사용&lt;/li&gt;
&lt;li&gt;글로벌로 제어되는 multi_compile 키워드는 카운팅되지 않음. (예 : _MAIN_LIGHT_SHWDOWS)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정확히 Material에 저장된 Keyword만 카운트됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;1069&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bP7Zgy/btsAIF7vTjc/pEkNbhNkiG4opB8tcKxrkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bP7Zgy/btsAIF7vTjc/pEkNbhNkiG4opB8tcKxrkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bP7Zgy/btsAIF7vTjc/pEkNbhNkiG4opB8tcKxrkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbP7Zgy%2FbtsAIF7vTjc%2FpEkNbhNkiG4opB8tcKxrkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;1069&quot; data-origin-width=&quot;575&quot; data-origin-height=&quot;1069&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1700570727257&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;

// Ref - https://github.com/needle-tools/shader-variant-explorer

public class ShaderVariantChecker : EditorWindow
{
    private static MethodInfo GetVariantCount, GetShaderGlobalKeywords, GetShaderLocalKeywords;

    private Shader _shader;
 
    private ulong _variantCount = 0;
    private ulong _usedVariantCount = 0;
    private int _keywordCount = 0;
    private int _materialCount = 0;
    private Dictionary&amp;lt;string, int&amp;gt; _materialKeywords = new Dictionary&amp;lt;string, int&amp;gt;();
    private bool _isChecked = false;

    private Vector2 scrollPosition;

    [MenuItem(&quot;CustomTool/ShaderVariantChecker&quot;)]
    private static void ShowWindow()
    {
        GetWindow&amp;lt;ShaderVariantChecker&amp;gt;().Show();
    }



    public void OnGUI()
    {
        _shader = EditorGUILayout.ObjectField(&quot;Shader&quot;, _shader, typeof(Shader), true) as Shader;
        if(GUILayout.Button(&quot;Check&quot;))
        {
            if (!_shader) return;
            Process(_shader);
        }

        if (!_isChecked) return;
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.LabelField(&quot;Variant Count : &quot; + _variantCount);
        EditorGUILayout.LabelField(&quot;Used Variant Count : &quot; + _usedVariantCount);
        EditorGUILayout.LabelField(&quot;Keyword Count : &quot; + _keywordCount);

        EditorGUILayout.EndHorizontal();

        EditorGUILayout.LabelField(&quot;Used Material Count : &quot; + _materialCount);
        scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);

        Color defaultColor = GUI.color;
        foreach (KeyValuePair&amp;lt;string, int&amp;gt; keyword in _materialKeywords)
        {
            EditorGUILayout.BeginHorizontal();
            bool isValid = keyword.Value &amp;gt; 0;
            if (isValid) GUI.color = Color.red;
            EditorGUILayout.TextField(keyword.Key);
            EditorGUILayout.TextField(keyword.Value.ToString(), GUILayout.Width(30));

            GUI.color = defaultColor;
            EditorGUILayout.EndHorizontal();
        }
        EditorGUILayout.EndScrollView();
    }

    private void Process(Shader shader)
    {
        if (!shader) return;

   
        GetShaderDetails(shader, out var variantCount, out var usedVariantCount, out string[] localKeywords, out string[] globalKeywords);
        
        _materialKeywords.Clear();
        for (int i = 0; i &amp;lt; localKeywords.Length; i++)
        {
            _materialKeywords.Add(localKeywords[i], 0);
        }
        for (int i = 0; i &amp;lt; globalKeywords.Length; i++)
        {
            if (_materialKeywords.ContainsKey(globalKeywords[i])) continue;
            _materialKeywords.Add(globalKeywords[i], 0);
        }
   
        _variantCount = variantCount;
        _usedVariantCount = usedVariantCount;
        _keywordCount = _materialKeywords.Count;

        // Debug.Log(&quot;variantCount : &quot; + variantCount + &quot;, usedVariantCount : &quot; + usedVariantCount + &quot;, keywordCount : &quot; + keywordTotalCount);

        List&amp;lt;Material&amp;gt; materials = FindMaterialsUsingShader(shader);
        _materialCount = materials.Count;
        for (int i=0; i&amp;lt; materials.Count; i++)
        {
            Material mat = materials[i];
            if (!mat) continue;
            string[] keywords = mat.shaderKeywords;
            if(keywords == null || keywords.Length ==0) continue;

            for(int j=0; j&amp;lt; keywords.Length; j++)
            {
                if (!_materialKeywords.ContainsKey(keywords[j])) continue;
                _materialKeywords[keywords[j]]++;
            }
        }

        _isChecked = true;
    }

    void GetShaderDetails(Shader requestedShader, out ulong shaderVariantCount, out ulong usedShaderVariantCount, out string[] localKeywords, out string[] globalKeywords)
    {
        if (GetVariantCount == null) GetVariantCount = typeof(ShaderUtil).GetMethod(&quot;GetVariantCount&quot;, (BindingFlags)(-1));
        if (GetShaderGlobalKeywords == null) GetShaderGlobalKeywords = typeof(ShaderUtil).GetMethod(&quot;GetShaderGlobalKeywords&quot;, (BindingFlags)(-1));
        if (GetShaderLocalKeywords == null) GetShaderLocalKeywords = typeof(ShaderUtil).GetMethod(&quot;GetShaderLocalKeywords&quot;, (BindingFlags)(-1));

        if (GetVariantCount == null || GetShaderGlobalKeywords == null || GetShaderLocalKeywords == null)
        {
            shaderVariantCount = 0;
            usedShaderVariantCount = 0;
            localKeywords = null;
            globalKeywords = null;
            return;
        }

        shaderVariantCount = (ulong)GetVariantCount.Invoke(null, new object[] { requestedShader, false });
        usedShaderVariantCount = (ulong)GetVariantCount.Invoke(null, new object[] { requestedShader, true });
        localKeywords = (string[])GetShaderLocalKeywords.Invoke(null, new object[] { requestedShader });
        globalKeywords = (string[])GetShaderGlobalKeywords.Invoke(null, new object[] { requestedShader });

        // var name = $&quot;{requestedShader.name}: ({shaderVariantCount} variants, {localKeywords.Length} local, {globalKeywords.Length} global)&quot;;
    }

    public static List&amp;lt;Material&amp;gt; FindMaterialsUsingShader(Shader shader)
    {
        List&amp;lt;Material&amp;gt; materialsUsingShader = new List&amp;lt;Material&amp;gt;();
        string[] materialAssetGUIDs = AssetDatabase.FindAssets(&quot;t:Material&quot;);
        foreach (var guid in materialAssetGUIDs)
        {
            string assetPath = AssetDatabase.GUIDToAssetPath(guid);
            Material material = AssetDatabase.LoadAssetAtPath&amp;lt;Material&amp;gt;(assetPath);

            if (material != null &amp;amp;&amp;amp; material.shader != null)
            { 
                if (material.shader.name == shader.name)
                {
                    materialsUsingShader.Add(material);
                }
            }
        }

        return materialsUsingShader;
    }

}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Unity/Programming</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/158</guid>
      <comments>https://darkcatgame.tistory.com/158#entry158comment</comments>
      <pubDate>Tue, 21 Nov 2023 21:48:12 +0900</pubDate>
    </item>
    <item>
      <title>SRP Batcher GLES &amp;amp; Vulkan 비교</title>
      <link>https://darkcatgame.tistory.com/157</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티의 SRP Batcher가 OpenGLES와 Vulkan API에서 차이점과 기타 특이사항을 정리했습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 환경&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity 2022.1.22f1 / URP 13.1.8&lt;/li&gt;
&lt;li&gt;갤럭시s10e / ARM Mali-G76&lt;/li&gt;
&lt;li&gt;Unity Profiler / RenderDoc&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SRP Batcher 기능과 성능 측정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRP Batcher는 Render States 변동이 필요 없는 경우 추가 명령을 생성하지 않는 방식으로 최적화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Render States 변동 여부는 유니티 엔진에서는 SetPass Calls 수치로 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRP Batcher 유무에 따라 RenderDoc에서 API 호출 상태를 비교해보겠습니다.&lt;u&gt;&lt;b&gt; (OpenGLES 빌드)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uwnSk/btsAmKOLp72/YuwlOkoc7I5ROTQV3lLyW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uwnSk/btsAmKOLp72/YuwlOkoc7I5ROTQV3lLyW0/img.png&quot; data-alt=&quot;Renderer 9개 / Mesh 1종 / Material 9종&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uwnSk/btsAmKOLp72/YuwlOkoc7I5ROTQV3lLyW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuwnSk%2FbtsAmKOLp72%2FYuwlOkoc7I5ROTQV3lLyW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;614&quot; height=&quot;327&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Renderer 9개 / Mesh 1종 / Material 9종&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 엔진에서 Draw calls와 SetPass Calls는 RenderDoc에서 렌더 이벤트와 그 내부의 렌더 API 호출 내용으로 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 경우는 첫번째 렌더 이벤트에서 Draw calls와 SetPass Calls API 명령이 전부 들어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdHN4u/btsAm0qmwdX/GwHl9Aib8E7knWsF8uwWOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdHN4u/btsAm0qmwdX/GwHl9Aib8E7knWsF8uwWOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdHN4u/btsAm0qmwdX/GwHl9Aib8E7knWsF8uwWOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdHN4u%2FbtsAm0qmwdX%2FGwHl9Aib8E7knWsF8uwWOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;681&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 이벤트부터 SRP Batcher 유무 차이를 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRP Batcher를 활성화 상태인 경우 Render States 변경 명령이 없어 API 갯수가 줄어들 었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1611&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3mz9y/btsAksU9Dno/Yvd5q8m9J9S2bAQa8vBOsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3mz9y/btsAksU9Dno/Yvd5q8m9J9S2bAQa8vBOsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3mz9y/btsAksU9Dno/Yvd5q8m9J9S2bAQa8vBOsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3mz9y%2FbtsAksU9Dno%2FYvd5q8m9J9S2bAQa8vBOsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1611&quot; height=&quot;635&quot; data-origin-width=&quot;1611&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderDoc의 Statistics창에서 최종 렌더 정보 요약을 확인 할 수 있는데 여기서도 API calls 갯수 차이를 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lJas0/btsAmpDYzJe/KuktIYnCart0kdfewEhi21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lJas0/btsAmpDYzJe/KuktIYnCart0kdfewEhi21/img.png&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;288&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.8329%; margin-right: 10px;&quot; data-widthpercent=&quot;50.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lJas0/btsAmpDYzJe/KuktIYnCart0kdfewEhi21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlJas0%2FbtsAmpDYzJe%2FKuktIYnCart0kdfewEhi21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5YAUZ/btsAiCxAfER/YPFxdjsCdDkfV9Wxd6KdW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5YAUZ/btsAiCxAfER/YPFxdjsCdDkfV9Wxd6KdW0/img.png&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;294&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0043%;&quot; data-widthpercent=&quot;49.58&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5YAUZ/btsAiCxAfER/YPFxdjsCdDkfV9Wxd6KdW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5YAUZ%2FbtsAiCxAfER%2FYPFxdjsCdDkfV9Wxd6KdW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : SRP Batcher Off 우 : SRP Batcher On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 유니티 프로파일러에서 성능 비교입니다. 확실한 비교를 위해 200개의 오브젝트 세팅했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;573&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLTm2c/btsAmzmbPQh/jAGJZZikyARBNMSUyjQtGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLTm2c/btsAmzmbPQh/jAGJZZikyARBNMSUyjQtGk/img.png&quot; data-alt=&quot;Renderer 200개 / Mesh 1종 / Material 200종&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLTm2c/btsAmzmbPQh/jAGJZZikyARBNMSUyjQtGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLTm2c%2FbtsAmzmbPQh%2FjAGJZZikyARBNMSUyjQtGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1190&quot; height=&quot;573&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;573&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Renderer 200개 / Mesh 1종 / Material 200종&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Draw calls 및 SetPass Calls에 의한 성능 측정은 CPU Usage의 Render Loop 항목을 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRP Batcher를 활성화하는 경우 그래프 스파이크 폭이 줄어들고 메인 쓰레드와 렌더 쓰레드의 Render Loop 소요 시간이 줄어 들었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coy75f/btsAjvdzXp8/vXPOLjoB2HQJkrr0mUZHGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coy75f/btsAjvdzXp8/vXPOLjoB2HQJkrr0mUZHGK/img.png&quot; data-alt=&quot;SRP Batcher Off&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coy75f/btsAjvdzXp8/vXPOLjoB2HQJkrr0mUZHGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcoy75f%2FbtsAjvdzXp8%2FvXPOLjoB2HQJkrr0mUZHGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1207&quot; height=&quot;439&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher Off&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/txRN0/btsAjOD9elM/UzPD6tkBVzKFKlkSfxBsV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/txRN0/btsAjOD9elM/UzPD6tkBVzKFKlkSfxBsV1/img.png&quot; data-alt=&quot;SRP Batcher On&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/txRN0/btsAjOD9elM/UzPD6tkBVzKFKlkSfxBsV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtxRN0%2FbtsAjOD9elM%2FUzPD6tkBVzKFKlkSfxBsV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1210&quot; height=&quot;416&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Material을 1종 사용하는 상황에 SRP Batcher 성능 비교&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Renderer가 동일한 Material 1종을 사용하는 상황. 즉, 애초부터 SetPass Calls가 1인 상황에 SRP Batcher 성능 비교에 대해서 정리했습니다. &lt;u&gt;&lt;b&gt;(OpenGLES 빌드)&lt;/b&gt;&lt;/u&gt; &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YVdL1/btsAmFfDSfe/ZSqzamxDwTTDK8XUX5DRz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YVdL1/btsAmFfDSfe/ZSqzamxDwTTDK8XUX5DRz1/img.png&quot; data-alt=&quot;Renderer 200개 / Mesh 1종 / Material 1종&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YVdL1/btsAmFfDSfe/ZSqzamxDwTTDK8XUX5DRz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYVdL1%2FbtsAmFfDSfe%2FZSqzamxDwTTDK8XUX5DRz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1174&quot; height=&quot;565&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Renderer 200개 / Mesh 1종 / Material 1종&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderDoc API 호출 비교입니다. 유니티 Stats에서 SetPass Calls 수치는 동일하지만 실제로 API 호출 수치는 SRP Batcher가 활성화했을 때 더 줄어듭니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5jwNj/btsAmxPvFuS/s94KJxy8AqsGaucz6fipbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5jwNj/btsAmxPvFuS/s94KJxy8AqsGaucz6fipbk/img.png&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;522&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.5901%; margin-right: 10px;&quot; data-widthpercent=&quot;53.21&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5jwNj/btsAmxPvFuS/s94KJxy8AqsGaucz6fipbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5jwNj%2FbtsAmxPvFuS%2Fs94KJxy8AqsGaucz6fipbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;953&quot; height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sfnxy/btsAmCJYOER/kLkHkVzEsaksDZFghpKVKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sfnxy/btsAmCJYOER/kLkHkVzEsaksDZFghpKVKk/img.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;877&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.2471%;&quot; data-widthpercent=&quot;46.79&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sfnxy/btsAmCJYOER/kLkHkVzEsaksDZFghpKVKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsfnxy%2FbtsAmCJYOER%2FkLkHkVzEsaksDZFghpKVKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1408&quot; height=&quot;877&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : SRP Batcher Off 우 : SRP Batcher On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XBgnY/btsAmqiCkIa/65kKZV6spCL7qIXeyo7wrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XBgnY/btsAmqiCkIa/65kKZV6spCL7qIXeyo7wrk/img.png&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;346&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.2368%; margin-right: 10px;&quot; data-widthpercent=&quot;49.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XBgnY/btsAmqiCkIa/65kKZV6spCL7qIXeyo7wrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXBgnY%2FbtsAmqiCkIa%2F65kKZV6spCL7qIXeyo7wrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXBAAx/btsAmFGJ8lK/8kJBuIK3M0dyR8SZs3RRBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXBAAx/btsAmFGJ8lK/8kJBuIK3M0dyR8SZs3RRBK/img.png&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;340&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.6004%;&quot; data-widthpercent=&quot;50.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXBAAx/btsAmFGJ8lK/8kJBuIK3M0dyR8SZs3RRBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXBAAx%2FbtsAmFGJ8lK%2F8kJBuIK3M0dyR8SZs3RRBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : SRP Batcher Off 우 : SRP Batcher On &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 프로파일러에서 또한 SRP Bathcer를 활성화했을 때 Render Loop 소요 시간이 더 줄어든 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DNNMO/btsAhganAg5/atzKz5eqAprLYiWoLT1a9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DNNMO/btsAhganAg5/atzKz5eqAprLYiWoLT1a9K/img.png&quot; data-alt=&quot;SRP Batcher Off&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DNNMO/btsAhganAg5/atzKz5eqAprLYiWoLT1a9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDNNMO%2FbtsAhganAg5%2FatzKz5eqAprLYiWoLT1a9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1207&quot; height=&quot;432&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher Off&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J0Oyw/btsAowbbcDA/Dp5spUrNagaEdJucFjWXp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J0Oyw/btsAowbbcDA/Dp5spUrNagaEdJucFjWXp0/img.png&quot; data-alt=&quot;SRP Batcher On&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J0Oyw/btsAowbbcDA/Dp5spUrNagaEdJucFjWXp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ0Oyw%2FbtsAowbbcDA%2FDp5spUrNagaEdJucFjWXp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1211&quot; height=&quot;424&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Vulkan API 환경에서 SRP Batcher 결과 비교&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 내용은 전부 OpenGLES 환경에서 측정한 데이터입니다. Vulkan API 환경에서는 여러가지 다른 결과가 측정되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 환경은 이전과 동일하게 SetPass Calls가 1인 상황입니다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YVdL1/btsAmFfDSfe/ZSqzamxDwTTDK8XUX5DRz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YVdL1/btsAmFfDSfe/ZSqzamxDwTTDK8XUX5DRz1/img.png&quot; data-alt=&quot;Renderer 200개 / Mesh 1종 / Material 1종&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YVdL1/btsAmFfDSfe/ZSqzamxDwTTDK8XUX5DRz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYVdL1%2FbtsAmFfDSfe%2FZSqzamxDwTTDK8XUX5DRz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1174&quot; height=&quot;565&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Renderer 200개 / Mesh 1종 / Material 1종&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderDoc에서 API 호출 내용을 비교해보면 SRP Batcher On/Off 결과가 동일하게 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1702&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbNoDM/btsAmpYgLJu/xrRZsA8rTUCBfdmW2EOS3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbNoDM/btsAmpYgLJu/xrRZsA8rTUCBfdmW2EOS3k/img.png&quot; data-alt=&quot;SRP Batcher Off&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbNoDM/btsAmpYgLJu/xrRZsA8rTUCBfdmW2EOS3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbNoDM%2FbtsAmpYgLJu%2FxrRZsA8rTUCBfdmW2EOS3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1702&quot; height=&quot;820&quot; data-origin-width=&quot;1702&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher Off&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbUp8h/btsAeCxZh3f/eukaZBeYS8rV89rOUeJAdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbUp8h/btsAeCxZh3f/eukaZBeYS8rV89rOUeJAdk/img.png&quot; data-alt=&quot;SRP Batcher On&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbUp8h/btsAeCxZh3f/eukaZBeYS8rV89rOUeJAdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbUp8h%2FbtsAeCxZh3f%2FeukaZBeYS8rV89rOUeJAdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1698&quot; height=&quot;820&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Statisics정보입니다, 이전 OpenGLES에서는 SRP Batcher가 Off 상황에 API calls가 600개 측정되었지만 Vulkan에서는 SRP Batcher On/Off 둘다 API calls가 약 260개가 측정되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 Vulkan API 환경에서는 자동으로 불필요한 RenderStates 변경 명령이 호출되지 않는 것으로 보입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJ0TpV/btsAjV4mWIu/854bkIEIGqwojLTwA3r3tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJ0TpV/btsAjV4mWIu/854bkIEIGqwojLTwA3r3tk/img.png&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;377&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.7005%; margin-right: 10px;&quot; data-widthpercent=&quot;47.25&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJ0TpV/btsAjV4mWIu/854bkIEIGqwojLTwA3r3tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJ0TpV%2FbtsAjV4mWIu%2F854bkIEIGqwojLTwA3r3tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMJHts/btsAooYBTIL/1DkcIbKQwlSd3nkkb9f6B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMJHts/btsAooYBTIL/1DkcIbKQwlSd3nkkb9f6B0/img.png&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;336&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.1367%;&quot; data-widthpercent=&quot;52.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMJHts/btsAooYBTIL/1DkcIbKQwlSd3nkkb9f6B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMJHts%2FbtsAooYBTIL%2F1DkcIbKQwlSd3nkkb9f6B0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : SRP Batcher Off 우 : SRP Batcher On &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 프로파일러에서도 SRP Batcher On/Off 결과가 거의 동일하게 측정되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH3eps/btsAnArrrYU/xHpJWctMSPHru7SZFLtd5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH3eps/btsAnArrrYU/xHpJWctMSPHru7SZFLtd5k/img.png&quot; data-alt=&quot;SRP Batcher Off&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH3eps/btsAnArrrYU/xHpJWctMSPHru7SZFLtd5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH3eps%2FbtsAnArrrYU%2FxHpJWctMSPHru7SZFLtd5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1211&quot; height=&quot;435&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;435&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher Off&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn1Dz9/btsAorOyRrD/4Gezt42bzAjy1mnL3LKxY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn1Dz9/btsAorOyRrD/4Gezt42bzAjy1mnL3LKxY1/img.png&quot; data-alt=&quot;SRP Batcher On&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn1Dz9/btsAorOyRrD/4Gezt42bzAjy1mnL3LKxY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn1Dz9%2FbtsAorOyRrD%2F4Gezt42bzAjy1mnL3LKxY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1210&quot; height=&quot;437&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 개별 Material을 사용하는 SetPass Calls가 200인 테스트 환경입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cf87G0/btsAi3aPeqE/UWnX1q9skIJBeBUM58KobK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cf87G0/btsAi3aPeqE/UWnX1q9skIJBeBUM58KobK/img.png&quot; data-alt=&quot;Renderer 200개 / Mesh 1종 / Material 200종&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cf87G0/btsAi3aPeqE/UWnX1q9skIJBeBUM58KobK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf87G0%2FbtsAi3aPeqE%2FUWnX1q9skIJBeBUM58KobK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;378&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Renderer 200개 / Mesh 1종 / Material 200종&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderDoc API 호출 내용은 SRP Batcher On/Off 둘다 동일합니다.(세부 내용 차이가 조금 있음)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1711&quot; data-origin-height=&quot;823&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/of9Yv/btsAeBFSmkq/pAYwOEdMQkKcFdG9M4Wa9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/of9Yv/btsAeBFSmkq/pAYwOEdMQkKcFdG9M4Wa9k/img.png&quot; data-alt=&quot;SRP Batcher Off&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/of9Yv/btsAeBFSmkq/pAYwOEdMQkKcFdG9M4Wa9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fof9Yv%2FbtsAeBFSmkq%2FpAYwOEdMQkKcFdG9M4Wa9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1711&quot; height=&quot;823&quot; data-origin-width=&quot;1711&quot; data-origin-height=&quot;823&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher Off&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mQ0VK/btsAjgHL62A/nTkfr4R0CycwK5s6UijKQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mQ0VK/btsAjgHL62A/nTkfr4R0CycwK5s6UijKQ0/img.png&quot; data-alt=&quot;SRP Batcher On&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mQ0VK/btsAjgHL62A/nTkfr4R0CycwK5s6UijKQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmQ0VK%2FbtsAjgHL62A%2FnTkfr4R0CycwK5s6UijKQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1708&quot; height=&quot;820&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRP Batcher On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ucc8v/btsAklBO0Yl/J9hgcQo2vBupBEUkWKCftK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ucc8v/btsAklBO0Yl/J9hgcQo2vBupBEUkWKCftK/img.png&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;332&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.7913%; margin-right: 10px;&quot; data-widthpercent=&quot;46.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ucc8v/btsAklBO0Yl/J9hgcQo2vBupBEUkWKCftK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUcc8v%2FbtsAklBO0Yl%2FJ9hgcQo2vBupBEUkWKCftK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2r5TS/btsAjuZ6TYM/sTR9My49Cf7NxOnmCKtXQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2r5TS/btsAjuZ6TYM/sTR9My49Cf7NxOnmCKtXQ1/img.png&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;289&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.0459%;&quot; data-widthpercent=&quot;53.67&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2r5TS/btsAjuZ6TYM/sTR9My49Cf7NxOnmCKtXQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2r5TS%2FbtsAjuZ6TYM%2FsTR9My49Cf7NxOnmCKtXQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;601&quot; height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : SRP Batcher Off 우 : SRP Batcher On &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니티 프로파일러에서는 그래프 스파이크 폭과 성능이 SRP Batcher가 더 개선된 것으로 확인됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tZuGJ/btsAm647axB/0oVMKut4o96HxjZ1OF4QCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tZuGJ/btsAm647axB/0oVMKut4o96HxjZ1OF4QCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tZuGJ/btsAm647axB/0oVMKut4o96HxjZ1OF4QCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtZuGJ%2FbtsAm647axB%2F0oVMKut4o96HxjZ1OF4QCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1207&quot; height=&quot;422&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1MS1l/btsAmJoPBfF/9yRFSSqJtq9qC9p9vGvkwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1MS1l/btsAmJoPBfF/9yRFSSqJtq9qC9p9vGvkwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1MS1l/btsAmJoPBfF/9yRFSSqJtq9qC9p9vGvkwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1MS1l%2FbtsAmJoPBfF%2F9yRFSSqJtq9qC9p9vGvkwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1207&quot; height=&quot;397&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 Vulkan API 환경에서는 SRP Batcher 성능 비교 결과, RenderDoc에서 측정한 API 호출 리스트는 거의 동일하지만 유니티 프로파일러에서는 성능 차이가 확인됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderDoc이 Vulkan API 환경에서 RenderStates 변화가 정확한 측정이 되는지 확인하기 위해 2개의 다른 쉐이더를 세팅한 환경을 테스트했습니다. 이 테스트 환경은 SRP Batcher를 사용해도 SetPass Calls가 증가되기 때문에 RenderDoc에서는 어떤 결과가 나오는지 확인하기 위함입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPUT3f/btsAjiFDABE/Pu1NrA28BhJZ4KvSiea191/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPUT3f/btsAjiFDABE/Pu1NrA28BhJZ4KvSiea191/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPUT3f/btsAjiFDABE/Pu1NrA28BhJZ4KvSiea191/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPUT3f%2FbtsAjiFDABE%2FPu1NrA28BhJZ4KvSiea191%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;990&quot; height=&quot;423&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderDoc에서는 정확히 다른 쉐이더가 렌더링되는 이벤트에 API가 변화가 확인되었습니다. RenderStates 변화 측정은 되긴 하는 것 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2016&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ei9yDf/btsAjUK9SqS/FDckMTwG4CgECBDlyEuHNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ei9yDf/btsAjUK9SqS/FDckMTwG4CgECBDlyEuHNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ei9yDf/btsAjUK9SqS/FDckMTwG4CgECBDlyEuHNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fei9yDf%2FbtsAjUK9SqS%2FFDckMTwG4CgECBDlyEuHNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2016&quot; height=&quot;297&quot; data-origin-width=&quot;2016&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SRP Batcher는 불필요한 Render States 명령 호출을 줄여 렌더링 최적화하는 시스템으로 유니티 엔진에서는 SetPass Calls 수치로 확인가능하고 RenderDoc에서 API 호출 상태를 통해 확인 가능.&lt;/li&gt;
&lt;li&gt;OpenGLES 기준, SetPass Calls가 1인 상태에서도 SRP Batcher가 활성화되는 경우 렌더 명령 API 호출이 줄어들고 실제 성능이 개선됨.&lt;/li&gt;
&lt;li&gt;Vulkan 기준, SRP Batcher를 활성화하지 않아도 불필요한 Render State 명령 호출이 줄어들고 실제 성능이 개선되는 결과가 확인됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; 프로파일링 성능 측정 결과는 상황과 환경에 따라 다르게 측정되기 때문에 테스트 내용과 결과 참고용으로만 봐주세요,&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;notranslate&quot; style=&quot;all: initial;&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>Unity/TA</category>
      <category>RenderDoc</category>
      <category>SetPass Calls</category>
      <category>SRP Batcher</category>
      <category>드로우콜</category>
      <category>유니티 프로파일러</category>
      <category>최적화</category>
      <category>프로파일러</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/157</guid>
      <comments>https://darkcatgame.tistory.com/157#entry157comment</comments>
      <pubDate>Tue, 14 Nov 2023 23:34:11 +0900</pubDate>
    </item>
    <item>
      <title>CustomInspector CategoryHeaderGUI - 어두운 폴드바GUI</title>
      <link>https://darkcatgame.tistory.com/156</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NBjHh/btsypxDa8tC/n4Zigt7AxiNDaXkHVuVaP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NBjHh/btsypxDa8tC/n4Zigt7AxiNDaXkHVuVaP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NBjHh/btsypxDa8tC/n4Zigt7AxiNDaXkHVuVaP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNBjHh%2FbtsypxDa8tC%2Fn4Zigt7AxiNDaXkHVuVaP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;444&quot; height=&quot;280&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity 엔진 내장 에디터툴에서 자주 사용되는 폴드바 GUI 에디터 스크립트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EditorGUILayout과 같은 기본 에디터 클래스에는 구현되어 있지 않아 직접 제작해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GUI 클래스&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1697117195719&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static class CategoryHeader
{
    protected static class Styles
    {
        public static readonly GUIStyle foldoutStyle;
        public static readonly GUIStyle categoryHeader;

        static Styles()
        {
            var builtInSkin = GetCurrentSkin();
            foldoutStyle = new GUIStyle(EditorStyles.foldout);
            foldoutStyle.fontStyle = FontStyle.Bold;

            categoryHeader = new GUIStyle(builtInSkin.label);
            categoryHeader.fontStyle = FontStyle.Bold;
            categoryHeader.border.left = 2;
            categoryHeader.padding.left = 32;
            categoryHeader.padding.top = 2;
            categoryHeader.border.right = 2;
  
            categoryHeader.normal.background = GetBackgroundProcedural_Twotone(new Color(0.196f, 0.196f, 0.196f),
                                                                                new Color(0.121f, 0.121f, 0.121f));
        }

        private static Texture2D GetBackgroundProcedural(Color color)
        {
            Texture2D texture = new Texture2D(1, 1);
            texture.SetPixel(0, 0, color);
            texture.Apply();
            return texture;
        }

        private static Texture2D GetBackgroundProcedural_Twotone(Color color, Color headerColor)
        {
            const int height = 16;
            Texture2D texture = new Texture2D(1, height);
            for(int i=0; i&amp;lt; height - 1; i++)
            {
                texture.SetPixel(0, i, color);
            }
            texture.SetPixel(0, height - 1, headerColor);
            texture.Apply();
            return texture;
        }

        private static Texture2D GetBackgroundAsset(string assetPath)
        {
            Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath&amp;lt;Texture2D&amp;gt;(assetPath);
            if (!texture)
            {
                Debug.LogWarning(&quot;GUITexture Asset does not exist : &quot; + assetPath);
                texture = null;
            }
            return texture;
        }

        public static GUISkin GetCurrentSkin()
        {
            return EditorGUIUtility.isProSkin ? EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene) : EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
        }
    }

    public static bool ShowHeader(GUIContent nameContent, bool foldoutState, string preferenceName = null)
    {
        float height = Styles.categoryHeader.CalcHeight(nameContent, 4000) + 3;

        Rect rect = GUILayoutUtility.GetRect(1, height - 1);
        rect.width += rect.x;
        rect.x = 0;

        if (Event.current.type == EventType.Repaint)
            Styles.categoryHeader.Draw(rect, nameContent, false, true, true, false);

        bool result = false;
        rect.x += 14;
        rect.width -= 2;
        result = EditorGUI.Toggle(rect, foldoutState, Styles.foldoutStyle);

        EditorGUI.indentLevel = result ? 1 : 0;

        if (preferenceName != null &amp;amp;&amp;amp; result != foldoutState)
        {
            EditorPrefs.SetBool(preferenceName, result);
        }

        return result;
    }

    public static bool ShowHeader(string label, bool foldoutState, string preferenceName = null)
    {
        return ShowHeader(EditorGUIUtility.TrTempContent(label), foldoutState, preferenceName);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용 샘플 &amp;amp; 전체 스크립트&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1697117251684&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;


[CustomEditor(typeof(CustomInspectorSample))]
public class CustomInspectorSample_Editor : Editor
{
    protected struct PropertyDefine
    {
        public SerializedProperty prop;
        public string path;

        public PropertyDefine(string path)
        {
            this.prop = null;
            this.path = path;
        }

        public void Init(SerializedObject serializedObject)
        {
            prop = serializedObject.FindProperty(path);
        }
    }

    protected static class Contents
    {
        public static readonly GUIContent headerGeneral = EditorGUIUtility.TrTextContent(&quot;General&quot;);
        public static readonly GUIContent amount = EditorGUIUtility.TrTextContent(&quot;Amount&quot;, &quot;Amount Property Description&quot;);
    }

    const string kGeneralFoldoutStatePreferenceName = &quot;CustomInspectorSample_Editor.Foldout.General&quot;;
    private bool showGeneralCategory;
    private bool showRendererCategory;

    private PropertyDefine _amountProp = new PropertyDefine(&quot;_amount&quot;);
    private PropertyDefine _colorProp = new PropertyDefine(&quot;_color&quot;);

    private CustomInspectorSample _component;
    

    private void OnEnable()
    {
        _component = (CustomInspectorSample)target;
        showGeneralCategory = EditorPrefs.GetBool(kGeneralFoldoutStatePreferenceName, true);
        
        _amountProp.Init(serializedObject);
        _colorProp.Init(serializedObject);
    }

   
    public override void OnInspectorGUI()
    { 
        if (!_component) return;
        serializedObject.Update();

        EditorGUI.BeginChangeCheck();
        GUILayout.Space(6);

        showGeneralCategory = CategoryHeader.ShowHeader(Contents.headerGeneral, showGeneralCategory, kGeneralFoldoutStatePreferenceName);
        if(showGeneralCategory)
        {
            EditorGUILayout.PropertyField(_amountProp.prop, Contents.amount);
            GUILayout.Space(6);
        }
        
        showRendererCategory = CategoryHeader.ShowHeader(&quot;Renderer&quot;, showRendererCategory);
        if (showRendererCategory)
        {
            EditorGUILayout.PropertyField(_colorProp.prop);
            GUILayout.Space(6);
        }

        if (EditorGUI.EndChangeCheck())
        {
            serializedObject.ApplyModifiedProperties();
        }   
    }
}

public static class CategoryHeader
{
    protected static class Styles
    {
        public static readonly GUIStyle foldoutStyle;
        public static readonly GUIStyle categoryHeader;

        static Styles()
        {
            var builtInSkin = GetCurrentSkin();
            foldoutStyle = new GUIStyle(EditorStyles.foldout);
            foldoutStyle.fontStyle = FontStyle.Bold;

            categoryHeader = new GUIStyle(builtInSkin.label);
            categoryHeader.fontStyle = FontStyle.Bold;
            categoryHeader.border.left = 2;
            categoryHeader.padding.left = 32;
            categoryHeader.padding.top = 2;
            categoryHeader.border.right = 2;
  
            categoryHeader.normal.background = GetBackgroundProcedural_Twotone(new Color(0.196f, 0.196f, 0.196f),
                                                                                new Color(0.121f, 0.121f, 0.121f));
        }

        private static Texture2D GetBackgroundProcedural(Color color)
        {
            Texture2D texture = new Texture2D(1, 1);
            texture.SetPixel(0, 0, color);
            texture.Apply();
            return texture;
        }

        private static Texture2D GetBackgroundProcedural_Twotone(Color color, Color headerColor)
        {
            const int height = 16;
            Texture2D texture = new Texture2D(1, height);
            for(int i=0; i&amp;lt; height - 1; i++)
            {
                texture.SetPixel(0, i, color);
            }
            texture.SetPixel(0, height - 1, headerColor);
            texture.Apply();
            return texture;
        }

        private static Texture2D GetBackgroundAsset(string assetPath)
        {
            Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath&amp;lt;Texture2D&amp;gt;(assetPath);
            if (!texture)
            {
                Debug.LogWarning(&quot;GUITexture Asset does not exist : &quot; + assetPath);
                texture = null;
            }
            return texture;
        }

        public static GUISkin GetCurrentSkin()
        {
            return EditorGUIUtility.isProSkin ? EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene) : EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
        }
    }

    public static bool ShowHeader(GUIContent nameContent, bool foldoutState, string preferenceName = null)
    {
        float height = Styles.categoryHeader.CalcHeight(nameContent, 4000) + 3;

        Rect rect = GUILayoutUtility.GetRect(1, height - 1);
        rect.width += rect.x;
        rect.x = 0;

        if (Event.current.type == EventType.Repaint)
            Styles.categoryHeader.Draw(rect, nameContent, false, true, true, false);

        bool result = false;
        rect.x += 14;
        rect.width -= 2;
        result = EditorGUI.Toggle(rect, foldoutState, Styles.foldoutStyle);

        EditorGUI.indentLevel = result ? 1 : 0;

        if (preferenceName != null &amp;amp;&amp;amp; result != foldoutState)
        {
            EditorPrefs.SetBool(preferenceName, result);
        }

        return result;
    }

    public static bool ShowHeader(string label, bool foldoutState, string preferenceName = null)
    {
        return ShowHeader(EditorGUIUtility.TrTempContent(label), foldoutState, preferenceName);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>Category GUI</category>
      <category>Category Header</category>
      <category>Custom Inspector</category>
      <category>Dark Color Fold</category>
      <category>Editor Script</category>
      <category>Inspector Fold</category>
      <category>unity</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/156</guid>
      <comments>https://darkcatgame.tistory.com/156#entry156comment</comments>
      <pubDate>Thu, 12 Oct 2023 22:28:33 +0900</pubDate>
    </item>
    <item>
      <title>Unity 2023-URP 16 렌더피처 API 변경 정리</title>
      <link>https://darkcatgame.tistory.com/155</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;URP 16.0.3 기준, ScriptableRenderFeature &amp;amp; Pass API가 변경되면서 이전 버전 코드를 수정하지 않으면 에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://forum.unity.com/threads/rendertargethandle-is-obsolete-deprecated-in-favor-of-rthandle.1211052/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://forum.unity.com/threads/rendertargethandle-is-obsolete-deprecated-in-favor-of-rthandle.1211052/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@16.0/manual/upgrade-guide-2023-2.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@16.0/manual/upgrade-guide-2023-2.html&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;RTHandle System도입으로 인한 ScriptableRenderPass 클래스 API 주요 변경 코드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;span style=&quot;color: #ee2323;&quot;&gt;빨강 = Before&lt;/span&gt;, &lt;span style=&quot;color: #409d00;&quot;&gt;초록 = After&lt;/span&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Change render target type&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;RenderTargetHandle renderTarget;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;RTHandle&amp;nbsp;renderTarget;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Associate the shader property with the render target using &lt;i&gt;RTHandles.Alloc&lt;/i&gt; instead of &lt;i&gt;renderTarget.Init&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;renderTarget.Init(&quot;_ShaderProperty&quot;);&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;renderTarget&amp;nbsp;=&amp;nbsp;RTHandles.Alloc(&quot;_ShaderProperty&quot;,&amp;nbsp;name:&amp;nbsp;&quot;_ShaderProperty&quot;);&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Assign temporary render texture using the shader property identifier&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;cmd.GetTemporaryRT(renderTarget.id,&amp;nbsp;targetDescriptor,&amp;nbsp;filterMode);&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;cmd.GetTemporaryRT(Shader.PropertyToID(renderTarget.name),&amp;nbsp;targetDescriptor,&amp;nbsp;filterMode);&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Modify ConfigureTarget to use the new RTHandle instead of the old RenderTargetIdentifier&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;ConfigureTarget(renderTarget.id);&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;ConfigureTarget(renderTarget);&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. Get the render texture identifier from the shader property&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;cmd.ReleaseTemporaryRT(renderTarget.id);&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;cmd.ReleaseTemporaryRT(Shader.PropertyToID(renderTarget.name));&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클래스 코드 예제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTHandle 도입 이전&lt;/p&gt;
&lt;pre id=&quot;code_1695381221970&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class CustomPass : ScriptableRenderPass
{
    RenderTargetHandle m_Handle;
    // With the old API, RenderTargetIdentifier might combine color and depth
    RenderTargetIdentifier m_Destination;

    public CustomPass()
    {
        m_Handle.Init(&quot;_CustomPassHandle&quot;);
    }

    public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    {
        var desc = renderingData.cameraData.cameraTargetDescriptor;
        cmd.GetTemporaryRT(m_Handle.id, desc, FilterMode.Point);
    }

    public override void OnCameraCleanup(CommandBuffer cmd)
    {
        cmd.ReleaseTemporaryRT(m_Handle.id);
    }

    public void Setup(RenderTargetIdentifier destination)
    {
        m_Destination = destination;
    }

    public override void Execute(ScriptableRenderContext context,
                                    ref RenderingData renderingData)
    {
        CommandBuffer cmd = CommandBufferPool.Get();
        // Set the same target for color and depth
        ScriptableRenderer.SetRenderTarget(cmd, m_Destination, m_Destination, clearFlag,
                                              clearColor);
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTHandle 도입 후&lt;/p&gt;
&lt;pre id=&quot;code_1695381281370&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class CustomPass : ScriptableRenderPass
{
    RTHandle m_Handle;
    // Then using RTHandles, the color and the depth properties must be separate
    RTHandle m_DestinationColor;
    RTHandle m_DestinationDepth;

    void Dispose()
    {
        m_Handle?.Release();
    }

    public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    {
        var desc = renderingData.cameraData.cameraTargetDescriptor;
        // Then using RTHandles, the color and the depth properties must be separate
        desc.depthBufferBits = 0;
        RenderingUtils.ReAllocateIfNeeded(ref m_Handle, desc, FilterMode.Point,
                                         TextureWrapMode.Clamp, name: &quot;_CustomPassHandle&quot;);
    }

    public override void OnCameraCleanup(CommandBuffer cmd)
    {
        m_DestinationColor = null;
        m_DestinationDepth = null;
    }

    public void Setup(RTHandle destinationColor, RTHandle destinationDepth)
    {
        m_DestinationColor = destinationColor;
        m_DestinationDepth = destinationDepth;
    }

    public override void Execute(ScriptableRenderContext context,
                                    ref RenderingData renderingData)
    {
        CommandBuffer cmd = CommandBufferPool.Get();
        ScriptableRenderer.SetRenderTarget(cmd, m_DestinationColor, m_DestinationDepth,
                                              clearFlag, clearColor);
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
     }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/155</guid>
      <comments>https://darkcatgame.tistory.com/155#entry155comment</comments>
      <pubDate>Fri, 22 Sep 2023 20:16:05 +0900</pubDate>
    </item>
    <item>
      <title>URP 15 데칼 레이어 분석 &amp;amp; 구현 방법</title>
      <link>https://darkcatgame.tistory.com/154</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbr2uK/btssDPYME2k/Y5TXLhced3tSXxpNGpCidk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbr2uK/btssDPYME2k/Y5TXLhced3tSXxpNGpCidk/img.png&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;517&quot; data-is-animation=&quot;false&quot; width=&quot;645&quot; height=&quot;386&quot; style=&quot;width: 48.9221%; margin-right: 10px;&quot; data-widthpercent=&quot;49.5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbr2uK/btssDPYME2k/Y5TXLhced3tSXxpNGpCidk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbr2uK%2FbtssDPYME2k%2FY5TXLhced3tSXxpNGpCidk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;863&quot; height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0hc0I/btssNuerf28/eTHZkNoT5FdiZYkOZERN8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0hc0I/btssNuerf28/eTHZkNoT5FdiZYkOZERN8k/img.png&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;512&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.9151%;&quot; data-widthpercent=&quot;50.5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0hc0I/btssNuerf28/eTHZkNoT5FdiZYkOZERN8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0hc0I%2FbtssNuerf28%2FeTHZkNoT5FdiZYkOZERN8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;872&quot; height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;https://youtu.be/QLrBoXCu6_Q?t=1233&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;데칼 레이어란&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데칼 레이어는 데칼을 사용할 때 지형에는 적용하면서 캐릭터를 예외 처리 하고 싶을 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unity 2023 URP 15에서 도입되었으며 Decal 렌더피쳐에서 옵션을 활성화 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 데칼 레이어 원리와 데칼 레이어를 지원하는 쉐이더 구현 방법을 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;URP 15 이전 데칼 레이어 구현 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhV2Ph/btssDRbc1rR/jemkL81Un6XmTrYIZYTofk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhV2Ph/btssDRbc1rR/jemkL81Un6XmTrYIZYTofk/img.png&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;369&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.4341%; margin-right: 10px;&quot; data-widthpercent=&quot;49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhV2Ph/btssDRbc1rR/jemkL81Un6XmTrYIZYTofk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhV2Ph%2FbtssDRbc1rR%2FjemkL81Un6XmTrYIZYTofk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BKoJn/btssDL29UNH/bflQAw6Wlz7aIWNxeqpUX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BKoJn/btssDL29UNH/bflQAw6Wlz7aIWNxeqpUX1/img.png&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;356&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.4031%;&quot; data-widthpercent=&quot;51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BKoJn/btssDL29UNH/bflQAw6Wlz7aIWNxeqpUX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBKoJn%2FbtssDL29UNH%2FbflQAw6Wlz7aIWNxeqpUX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 스텐실 테스트를 통해 데칼이 원하지 않은 오브젝트에 덮히지 않게 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 스텐실 넘버에 Replace 옵션으로 정보를 기록하고 데칼 마테리얼에서 NotEqual 옵션으로 스텐실 넘버가 겹치지 않은 부분만 렌더링하는 원리입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbI76D/btssHDXC3HN/FkLu1RdBjkgcp4Rt8wN83K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbI76D/btssHDXC3HN/FkLu1RdBjkgcp4Rt8wN83K/img.png&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;596&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.3857%; margin-right: 10px;&quot; data-widthpercent=&quot;51.99&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbI76D/btssHDXC3HN/FkLu1RdBjkgcp4Rt8wN83K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbI76D%2FbtssHDXC3HN%2FFkLu1RdBjkgcp4Rt8wN83K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;912&quot; height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSJZyr/btssMHZvnl3/UJdBhAxWyfTG40in8sgVC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSJZyr/btssMHZvnl3/UJdBhAxWyfTG40in8sgVC0/img.png&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;598&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.4515%;&quot; data-widthpercent=&quot;48.01&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSJZyr/btssMHZvnl3/UJdBhAxWyfTG40in8sgVC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSJZyr%2FbtssMHZvnl3%2FUJdBhAxWyfTG40in8sgVC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;845&quot; height=&quot;598&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : Before 우 : After&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더에 스텐실 제어하는 코드는 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693483576317&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; Properties
    { 
        _BaseColor(&quot;Color&quot;, Color) = (1, 1, 1, 1)
        _BaseMap(&quot;Texture&quot;, 2D) = &quot;white&quot; {}

        [Header(Stencil Test)]
        [Space(10)]
        [IntRange] _Reference(&quot;Ref&quot;, Range(0,255)) = 0
        [Enum(UnityEngine.Rendering.CompareFunction)] _Comp(&quot;Comp&quot;, float) = 8
        [Enum(UnityEngine.Rendering.StencilOp)] _Pass(&quot;Pass&quot;, float) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _fail(&quot;fail&quot;, float) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _Zfail(&quot;Zfail&quot;, float) = 0
    }
    
     Pass
        {
            Name  &quot;ScreenSpaceDecal&quot;
            Tags {&quot;LightMode&quot; = &quot;SRPDefaultUnlit&quot;}

            Cull back
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha 

             Stencil
            {
                Ref [_Reference] 
                Comp [_Comp] 
                Pass [_Pass] 
                fail [_fail] 
                Zfail [_Zfail]
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;URP 15 데칼 레이어 원리 분석&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 15의 데칼 레이어는 스텐실 방식이 아닌 별도의 레이어 버퍼를 추가로 사용하여 데칼 쉐이더 내부에서 필터링하는 방식입니다. Decal 렌더피쳐에서 데칼 레이어 옵션을 활성화하면 아래와 같이 _CameraRenderingLayerTexture 버퍼가 추가로 생성되는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rIHCE/btssMPpBLgk/ow0Uq9Pc9ZewAIo3mYsHi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rIHCE/btssMPpBLgk/ow0Uq9Pc9ZewAIo3mYsHi1/img.png&quot; data-origin-width=&quot;994&quot; data-origin-height=&quot;468&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.7545%; margin-right: 10px;&quot; data-widthpercent=&quot;51.35&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rIHCE/btssMPpBLgk/ow0Uq9Pc9ZewAIo3mYsHi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrIHCE%2FbtssMPpBLgk%2Fow0Uq9Pc9ZewAIo3mYsHi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;994&quot; height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMzy98/btssMIRDZ7j/NKk5c4PShJqszFuoVRRUE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMzy98/btssMIRDZ7j/NKk5c4PShJqszFuoVRRUE0/img.png&quot; data-origin-width=&quot;1992&quot; data-origin-height=&quot;990&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.0827%;&quot; data-widthpercent=&quot;48.65&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMzy98/btssMIRDZ7j/NKk5c4PShJqszFuoVRRUE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMzy98%2FbtssMIRDZ7j%2FNKk5c4PShJqszFuoVRRUE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1992&quot; height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링레이어 버퍼는 UniversalRenderer에서 렌더패스 호출하고 있으며 데칼레이어 옵션이 활성화되면 Forward 렌더링 환경에서 OpaqueForwardPass 대신 RenderingLayer 기능이 추가된 OpaqueForwardPass가 렌더패스 호출됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1982&quot; data-origin-height=&quot;89&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLHOie/btssDOetNjs/GIF3wkRIPC2popBkiRK1O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLHOie/btssDOetNjs/GIF3wkRIPC2popBkiRK1O0/img.png&quot; data-alt=&quot;UniversalRenderer.cs / UniversalRenderer(생성자)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLHOie/btssDOetNjs/GIF3wkRIPC2popBkiRK1O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLHOie%2FbtssDOetNjs%2FGIF3wkRIPC2popBkiRK1O0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1982&quot; height=&quot;89&quot; data-origin-width=&quot;1982&quot; data-origin-height=&quot;89&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UniversalRenderer.cs / UniversalRenderer(생성자)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAjfLd/btssDMHPDCu/9ROXq6qWFmP4aI3ELTA7Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAjfLd/btssDMHPDCu/9ROXq6qWFmP4aI3ELTA7Ik/img.png&quot; data-alt=&quot;UniversalRenderer.cs / Setup&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAjfLd/btssDMHPDCu/9ROXq6qWFmP4aI3ELTA7Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAjfLd%2FbtssDMHPDCu%2F9ROXq6qWFmP4aI3ELTA7Ik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1988&quot; height=&quot;789&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;789&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UniversalRenderer.cs / Setup&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderingLayersPass 렌더패스에서는 렌더링 레이어 쉐이더 키워드를 활성화 시켜줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;573&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1II3v/btssHyhI3Gj/8sdJNpBHzJZe6VGlpZ50Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1II3v/btssHyhI3Gj/8sdJNpBHzJZe6VGlpZ50Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1II3v/btssHyhI3Gj/8sdJNpBHzJZe6VGlpZ50Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1II3v%2FbtssHyhI3Gj%2F8sdJNpBHzJZe6VGlpZ50Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;758&quot; height=&quot;573&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;573&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LitForward.hlsl를 보면 이전 버전과 다르게 쉐이더 결과를 return하는게 아닌 out 방식으로 변경되었고 렌더링 레이어 분기가 활성화되어 있으면 메쉬 레이어 정보를 추가로 내보냅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIMfIP/btssHhmTOsd/5BSxYhzRdevQBXDNr5z8p1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIMfIP/btssHhmTOsd/5BSxYhzRdevQBXDNr5z8p1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIMfIP/btssHhmTOsd/5BSxYhzRdevQBXDNr5z8p1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIMfIP%2FbtssHhmTOsd%2F5BSxYhzRdevQBXDNr5z8p1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;134&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckweSw/btssJkja6Bu/RIasoF3Pu7wKRxTYgKDXJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckweSw/btssJkja6Bu/RIasoF3Pu7wKRxTYgKDXJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckweSw/btssJkja6Bu/RIasoF3Pu7wKRxTYgKDXJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckweSw%2FbtssJkja6Bu%2FRIasoF3Pu7wKRxTYgKDXJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;625&quot; height=&quot;76&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GetMeshRenderingLayer() 함수는 Renderer 컴포넌트의 Rendering Layer Mask 정보 값이 들어옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보 값을 SV_Target1로 내보내면 _CameraRenderingLayerTexture 버퍼에 정보가 그려지게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DA4fp/btssDQQZZql/I55uDNXfDy3ShDw3VUZ6kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DA4fp/btssDQQZZql/I55uDNXfDy3ShDw3VUZ6kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DA4fp/btssDQQZZql/I55uDNXfDy3ShDw3VUZ6kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDA4fp%2FbtssDQQZZql%2FI55uDNXfDy3ShDw3VUZ6kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;465&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;465&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Opaque렌더링 이후에 렌더링되는 쉐이더에서 _CameraRenderingLayersTexture를 샘플링하여 렌더링레이어 정보를 사용할 수 있습니다. 데칼 쉐이더에서는 해당 버퍼를 샘플링하는 방식으로 필터링하는 방식으로 데칼 레이어가 구현되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기술은 데칼뿐만 아니라 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt;&lt;b&gt;레이어 필터링이 필요한 기능을 구현할 때 응용 가능&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;할 것으로 보입니다. &lt;br /&gt;(예를 들어 포스트프로세스에서 특정 오브젝트를 제외하고 효과를 입히기.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IyASe/btssB00AP0W/AfvE2aXyhEJJgLzpoay3i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IyASe/btssB00AP0W/AfvE2aXyhEJJgLzpoay3i1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IyASe/btssB00AP0W/AfvE2aXyhEJJgLzpoay3i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIyASe%2FbtssB00AP0W%2FAfvE2aXyhEJJgLzpoay3i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;766&quot; height=&quot;141&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/31PP0/btssIolQwmk/T7qLxcvViFzIGOdsI1JMlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/31PP0/btssIolQwmk/T7qLxcvViFzIGOdsI1JMlk/img.png&quot; data-alt=&quot;&amp;quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareRenderingLayerTexture.hlsl&amp;quot;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/31PP0/btssIolQwmk/T7qLxcvViFzIGOdsI1JMlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F31PP0%2FbtssIolQwmk%2FT7qLxcvViFzIGOdsI1JMlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;945&quot; height=&quot;317&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;&quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareRenderingLayerTexture.hlsl&quot;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;렌더링 레이어 사용 예제&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RenderingLayer Write지원하는 쉐이더 샘플&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1693485348333&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shader &quot;CatDarkGame/Unlit_WriteRenderingLayer&quot;
{
    Properties
    { 
        [MainTexture] _BaseMap(&quot;Texture&quot;, 2D) = &quot;white&quot; {}
        [MainColor] _BaseColor(&quot;Color&quot;, Color) = (1, 1, 1, 1)
    }

    SubShader
    {
         Tags
        {
            &quot;RenderType&quot; = &quot;Opaque&quot;
            &quot;RenderPipeline&quot; = &quot;UniversalPipeline&quot;
            &quot;UniversalMaterialType&quot; = &quot;Lit&quot;
            &quot;IgnoreProjector&quot; = &quot;True&quot;
        }

        Pass
        {
            Name  &quot;URPUnlit&quot;
            Tags {&quot;LightMode&quot; = &quot;UniversalForward&quot;}

            ZWrite on
            Cull back

            HLSLPROGRAM
            #pragma target 4.5
            #pragma exclude_renderers gles d3d9
            
            #pragma vertex vert
            #pragma fragment frag

            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;
            #include &quot;Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonLighting.hlsl&quot;
            #include_with_pragmas &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl&quot;


            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                float4 _BaseColor;
            CBUFFER_END

            TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);


            struct Attributes
            {
                float4 positionOS    : POSITION;
                float2 uv            : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS    : SV_POSITION;
                float2 uv            : TEXCOORD0;
            }; 
            

            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;

                float4 positionOS = input.positionOS;
                float3 positionWS = TransformObjectToWorld(positionOS.xyz);
                float4 positionCS = TransformWorldToHClip(positionWS);

                output.positionCS = positionCS;
                output.uv = input.uv;
                return output;
            }

            void frag(Varyings input, out half4 outColor : SV_Target0
                                        #ifdef _WRITE_RENDERING_LAYERS
                                            , out float4 outRenderingLayers : SV_Target1
                                        #endif
                                        )
            {
                float2 baseMapUV = input.uv.xy * _BaseMap_ST.xy + _BaseMap_ST.zw;
                float4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, baseMapUV);
                
                #ifdef _WRITE_RENDERING_LAYERS
                    uint renderingLayers = GetMeshRenderingLayer();
                    outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0);
                #endif

           
                float4 finalColor = texColor * _BaseColor;
                outColor = finalColor;
            }
            
            ENDHLSL
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데칼레이어 기능이 추가된 데칼 쉐이더 (Decal Projection 컴포넌트 전용)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1693485432097&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shader &quot;CatDarkGame/ScreenSpaceDecal&quot;
{
    Properties
    { 
        _BaseColor(&quot;Color&quot;, Color) = (1, 1, 1, 1)
        _BaseMap(&quot;Texture&quot;, 2D) = &quot;white&quot; {}
    }

    SubShader
    {
       Tags
        {
            &quot;RenderPipeline&quot;=&quot;UniversalPipeline&quot;
            &quot;PreviewType&quot;=&quot;Plane&quot;
            &quot;DisableBatching&quot;=&quot;False&quot;
            &quot;ShaderGraphShader&quot;=&quot;true&quot;
            &quot;ShaderGraphTargetId&quot;=&quot;UniversalDecalSubTarget&quot;
        }
        

        Pass
        {
            Name  &quot;DecalScreenSpaceProjector&quot;
            Tags {&quot;LightMode&quot; = &quot;DecalScreenSpaceProjector&quot;}


            Cull back
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha 

            HLSLPROGRAM
            
            #define DECAL_PROJECTOR

            #pragma target 4.5
            
            
            #pragma vertex vert 
            #pragma fragment frag

            #pragma multi_compile _ _DECAL_LAYERS

            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;
            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl&quot;
            #ifdef _DECAL_LAYERS
                #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareRenderingLayerTexture.hlsl&quot;
                #include &quot;Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderVariablesDecal.hlsl&quot;
            #endif

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                float4 _BaseColor;
            CBUFFER_END

            TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);


            struct Attributes
            {
                float4 positionOS    : POSITION;
                float2 uv            : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS    : SV_POSITION;
                float2 uv            : TEXCOORD0;
                float4 positionSS    : TEXCOORD1;
                float4 viewRayOS     : TEXCOORD2; 
                float3 cameraPosOS   : TEXCOORD3;
            }; 
            
            
            void GetObjectSpaceViewTransform(float3 positionVS, out float4 objectToViewDir, out float3 viewPositionOS)
            {
                float viewZ = positionVS.z;
                float4x4 viewToObjectMatrix = mul(UNITY_MATRIX_I_M, UNITY_MATRIX_I_V);
                objectToViewDir.xyz = mul((float3x3)viewToObjectMatrix, positionVS.xyz * -1).xyz;
                objectToViewDir.w = viewZ;
                viewPositionOS.xyz = mul(viewToObjectMatrix, float4(0.0f, 0.0f, 0.0f, 1.0f)).xyz; 
            }


            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;

                float4 positionOS = input.positionOS;
                float3 positionWS = TransformObjectToWorld(positionOS.xyz);
                float4 positionCS = TransformWorldToHClip(positionWS);
                
                float4 positionSS = ComputeScreenPos(positionCS);
               
                float3 positionVS = TransformWorldToView(positionWS);
                float4 objectToViewDir = 0;
                float3 viewPositionOS = 0;
                GetObjectSpaceViewTransform(positionVS, objectToViewDir, viewPositionOS);
                
                output.viewRayOS = objectToViewDir;
                output.cameraPosOS = viewPositionOS ;
                output.positionSS = positionSS;
                output.positionCS = positionCS;
                output.uv = input.uv;
                return output;
            }

            float4 frag(Varyings input) : SV_Target
            {
                float decalLayerClip = 0;
                #ifdef _DECAL_LAYERS
                    uint surfaceRenderingLayer = LoadSceneRenderingLayer(input.positionCS.xy);
                    uint projectorRenderingLayer = uint(UNITY_ACCESS_INSTANCED_PROP(Decal, _DecalLayerMaskFromDecal));
                    decalLayerClip = 1 - (surfaceRenderingLayer &amp;amp; projectorRenderingLayer);
                #endif
          
                float sceneRawDepth = SampleSceneDepth(input.positionSS.xy / input.positionSS.w).r; 
                float sceneDepthVS = LinearEyeDepth(sceneRawDepth, _ZBufferParams);
                float3 viewRayOS = (input.viewRayOS.xyz / input.viewRayOS.w) * sceneDepthVS;
                float3 decalSpaceScenePos = input.cameraPosOS.xyz + viewRayOS.xyz;
             
                float3 absDecalSpaceScenePos = abs(decalSpaceScenePos);
                float decalClipAlpha = max(max(absDecalSpaceScenePos.x, absDecalSpaceScenePos.y), absDecalSpaceScenePos.z);
                clip(0.5f - decalClipAlpha - decalLayerClip);
               
                #ifndef DECAL_PROJECTOR
                    float2 decalSpaceUV = decalSpaceScenePos.xy + 0.5f;
                #else
                    float2 decalSpaceUV = decalSpaceScenePos.xz + 0.5f;
                #endif
               
                float2 uv = decalSpaceUV.xy * _BaseMap_ST.xy + _BaseMap_ST.zw;
                uv = frac(uv);
              
                float4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);
                float4 finalColor = texColor * _BaseColor;
                return finalColor;
            }
            
            ENDHLSL
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>Decal</category>
      <category>Decal Layer</category>
      <category>RenderingLayer</category>
      <category>Unity 2023</category>
      <category>URP 15</category>
      <category>데칼</category>
      <category>데칼 레이어</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/154</guid>
      <comments>https://darkcatgame.tistory.com/154#entry154comment</comments>
      <pubDate>Thu, 31 Aug 2023 21:39:30 +0900</pubDate>
    </item>
    <item>
      <title>URP 15.0.5 데칼 레이어 렌더링 이슈</title>
      <link>https://darkcatgame.tistory.com/153</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이슈 개요&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Unity 2023.1.0 DX11 API&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcKNsm/btso1vgV9G9/qvbRhpRKhhzVyXps98qqNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcKNsm/btso1vgV9G9/qvbRhpRKhhzVyXps98qqNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcKNsm/btso1vgV9G9/qvbRhpRKhhzVyXps98qqNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcKNsm%2Fbtso1vgV9G9%2FqvbRhpRKhhzVyXps98qqNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;112&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 데칼 렌더피처에는 뎁스 정보를 활용하는 ScreenSpace와 디퍼드 렌더링의 데칼 버퍼를 활용하는 DBuffer 2가지 모드가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1545&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rnqeB/btso0OOzDBj/2buVP52xxrCCTjCfRgzsG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rnqeB/btso0OOzDBj/2buVP52xxrCCTjCfRgzsG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rnqeB/btso0OOzDBj/2buVP52xxrCCTjCfRgzsG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrnqeB%2Fbtso0OOzDBj%2F2buVP52xxrCCTjCfRgzsG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1545&quot; height=&quot;427&quot; data-origin-width=&quot;1545&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Screen Space 데칼 모드에서 Rendering Layer 기능을 활성화 상태로 Depth Priming Mode와 MSAA가 활성화되어 있으면 GameView에서만 Opaque 오브젝트가 렌더링되지 않는 이슈가 있다.&lt;br /&gt;뎁스 테스트를 하지 않는 Transparent 오브젝트는 렌더링이 잘된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데칼 레이어 구조&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URP 15버전 기준 Lit 쉐이더를 보면 Forward와 DepthNormal 패스에서 Mesh RenderingLayer 정보를 출력하는 코드가 있는데 데칼 레이어는 해당 정보를 활용해 레이어 필터링하는 방식이다.&lt;br /&gt;(GbufferPass에서도&amp;nbsp;출력하지만&amp;nbsp;디퍼드&amp;nbsp;렌더링&amp;nbsp;관련&amp;nbsp;내용은&amp;nbsp;생략)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_WRITE_RENDERING_LAYERS multi complie 분기처리 되어 있으며 쉐이더에서는 아래와 같이 RenderingLayers.hlsl파일을 Include하고 URP Core 렌더피처의 DrawObjectPass와 DepthNormalOnly에서 해당 키워드를 제어한다 .&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(URP 15에서는 hlsl파일 내부에서 #pragma 구문까지 읽을 수 있는 include 문법이 가능하다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Ae1V/btsoUK1jAkb/VZmHNlbARZy3uCdL7LUefK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Ae1V/btsoUK1jAkb/VZmHNlbARZy3uCdL7LUefK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Ae1V/btsoUK1jAkb/VZmHNlbARZy3uCdL7LUefK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Ae1V%2FbtsoUK1jAkb%2FVZmHNlbARZy3uCdL7LUefK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;96&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baNwjp/btsoToYjGNU/E3LIK8daoM6Tn7UBqio24k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baNwjp/btsoToYjGNU/E3LIK8daoM6Tn7UBqio24k/img.png&quot; data-alt=&quot;DrawObjectsPass.cs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baNwjp/btsoToYjGNU/E3LIK8daoM6Tn7UBqio24k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaNwjp%2FbtsoToYjGNU%2FE3LIK8daoM6Tn7UBqio24k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;220&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DrawObjectsPass.cs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RenderingLayer는&amp;nbsp;URP12버전에서&amp;nbsp;LightLayer&amp;nbsp;기능에서&amp;nbsp;발전된&amp;nbsp;시스템으로&amp;nbsp;원래&amp;nbsp;라이트&amp;nbsp;레이어&amp;nbsp;정보와&amp;nbsp;자기&amp;nbsp;자신&amp;nbsp;메쉬&amp;nbsp;레이어&amp;nbsp;정보를&amp;nbsp;비교하여&amp;nbsp;라이트&amp;nbsp;연산을&amp;nbsp;필터링하는&amp;nbsp;기능으로&amp;nbsp;활용되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 아래처럼 쉐이더 코드는 Lighting.hlsl에서 라이트 레이어가 일치하는지 검사하는 코드가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;271&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/REpep/btsoTqPnHox/sTOsksQGJGeMxPEmM1lNpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/REpep/btsoTqPnHox/sTOsksQGJGeMxPEmM1lNpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/REpep/btsoTqPnHox/sTOsksQGJGeMxPEmM1lNpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FREpep%2FbtsoTqPnHox%2FsTOsksQGJGeMxPEmM1lNpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;271&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;271&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;현재&amp;nbsp;URP15에서는&amp;nbsp;_WRITE_RENDERING_LAYERS&amp;nbsp;분기가&amp;nbsp;활성화되어&amp;nbsp;있을&amp;nbsp;경우&amp;nbsp;자기&amp;nbsp;자신&amp;nbsp;메쉬&amp;nbsp;레이어를&amp;nbsp;SV_Target1로&amp;nbsp;출력하게&amp;nbsp;되어&amp;nbsp;있고&amp;nbsp;데칼&amp;nbsp;레이어와&amp;nbsp;같이&amp;nbsp;다른&amp;nbsp;메쉬의&amp;nbsp;레이어&amp;nbsp;정보&amp;nbsp;읽기가&amp;nbsp;필요한&amp;nbsp;시스템이&amp;nbsp;활성화되면&amp;nbsp;자동으로&amp;nbsp;분기가&amp;nbsp;활성화된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IZztU/btsoTmsMPkX/wdXDDAOHNyVIbg6PuKhfI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IZztU/btsoTmsMPkX/wdXDDAOHNyVIbg6PuKhfI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IZztU/btsoTmsMPkX/wdXDDAOHNyVIbg6PuKhfI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIZztU%2FbtsoTmsMPkX%2FwdXDDAOHNyVIbg6PuKhfI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;457&quot; height=&quot;118&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zRKMB/btsoZMDyRTd/cpszr3QoboReYMZ2HEQhD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zRKMB/btsoZMDyRTd/cpszr3QoboReYMZ2HEQhD0/img.png&quot; data-alt=&quot;LitForwardPass.hlsl / LitPassFragment&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zRKMB/btsoZMDyRTd/cpszr3QoboReYMZ2HEQhD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzRKMB%2FbtsoZMDyRTd%2Fcpszr3QoboReYMZ2HEQhD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;75&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LitForwardPass.hlsl / LitPassFragment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;_WRITE_RENDERING_LAYERS&amp;nbsp;분기는&amp;nbsp;여러&amp;nbsp;패스&amp;nbsp;중에&amp;nbsp;먼저&amp;nbsp;렌더링되는&amp;nbsp;패스에만&amp;nbsp;활성화되고&amp;nbsp;나머지&amp;nbsp;패스는&amp;nbsp;비활성화&amp;nbsp;상태가&amp;nbsp;유지된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 DepthNormal 패스가 프리패스로 먼저 렌더링되면 Forward패스는 해당 분기가 비활성화 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이슈 발생 조건&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로&amp;nbsp;ScreenSpace&amp;nbsp;데칼은&amp;nbsp;뎁스&amp;nbsp;정보를&amp;nbsp;사용하지만&amp;nbsp;특이한&amp;nbsp;점은&amp;nbsp;MSAA를&amp;nbsp;활성화하면&amp;nbsp;DepthNormalPrepass가&amp;nbsp;활성화되어&amp;nbsp;DepthPass대신&amp;nbsp;DepthNormal&amp;nbsp;정보를&amp;nbsp;사용하게&amp;nbsp;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ScreenSpace 데칼가 데칼 레이어를 사용한다고 해서 DepthNormal 정보를 사용하는 이유를 못 찾았고 명확한 이유가 진짜 없으면 이 현상은 유니티 버그 가능성이 높을 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zOa8Y/btsoZMcuyUc/l9ziWz8ujgkhJ8g6sMaZp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zOa8Y/btsoZMcuyUc/l9ziWz8ujgkhJ8g6sMaZp0/img.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;339&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.9358%; margin-right: 10px;&quot; data-widthpercent=&quot;54.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zOa8Y/btsoZMcuyUc/l9ziWz8ujgkhJ8g6sMaZp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzOa8Y%2FbtsoZMcuyUc%2Fl9ziWz8ujgkhJ8g6sMaZp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;340&quot; height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NS1kA/btsoTlOd3fQ/U8w9ORenj1e23qKW73swN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NS1kA/btsoTlOd3fQ/U8w9ORenj1e23qKW73swN1/img.png&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;412&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.9014%;&quot; data-widthpercent=&quot;45.43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NS1kA/btsoTlOd3fQ/U8w9ORenj1e23qKW73swN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNS1kA%2FbtsoTlOd3fQ%2FU8w9ORenj1e23qKW73swN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;344&quot; height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : MSAA Off 우 : MSAA On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;문제는&amp;nbsp;데칼&amp;nbsp;레이어와&amp;nbsp;MSAA가&amp;nbsp;활성화된&amp;nbsp;상태로&amp;nbsp;Depth&amp;nbsp;Priming&amp;nbsp;Mode를&amp;nbsp;활성화하면&amp;nbsp;DepthNormalPrepass에&amp;nbsp;MSAA&amp;nbsp;처리가&amp;nbsp;들어가면서&amp;nbsp;렌더링되지&amp;nbsp;않는&amp;nbsp;이슈가&amp;nbsp;발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 동일하게 DepthNormalPrepass가 프레임 디버거에 찍히지만 이슈가 발생하는 경우 그냥 렌더링되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 DepthNormalPrepass에 정보가 없는 상태에서 뎁스 테스트 컬링이 동작하게 되어 불투명 오브젝트가 렌더링되지 않는 결과가 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O7oaI/btsoZobOHrQ/tUZuaAntV5NzDMUKqhs5d1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O7oaI/btsoZobOHrQ/tUZuaAntV5NzDMUKqhs5d1/img.png&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;332&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.4107%; margin-right: 10px;&quot; data-widthpercent=&quot;51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O7oaI/btsoZobOHrQ/tUZuaAntV5NzDMUKqhs5d1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO7oaI%2FbtsoZobOHrQ%2FtUZuaAntV5NzDMUKqhs5d1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ujo91/btsoXysFNFb/Fgly2JnaSoG1P9rFAP8301/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ujo91/btsoXysFNFb/Fgly2JnaSoG1P9rFAP8301/img.png&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;309&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;49&quot; style=&quot;width: 48.4265%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ujo91/btsoXysFNFb/Fgly2JnaSoG1P9rFAP8301/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fujo91%2FbtsoXysFNFb%2FFgly2JnaSoG1P9rFAP8301%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : Depth Priming Mode Off 우 : Depth Priming Mode On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;데칼&amp;nbsp;레이어&amp;nbsp;대신&amp;nbsp;DepthNormalPass를&amp;nbsp;사용하는&amp;nbsp;SSAO로&amp;nbsp;동일한&amp;nbsp;조건을&amp;nbsp;만들면&amp;nbsp;이슈가&amp;nbsp;발생하지&amp;nbsp;않고&amp;nbsp;렌더링된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이점을 보아선 DepthNormalPrepass에 MSAA가 적용되서 문제가 발생하는게 아니라 데칼 레이어 내부 시스템에 의한 문제로 원인이 좁혀진다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/phpHH/btso23LeIT7/KUOk8S1l3AiWsIRrn8K8a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/phpHH/btso23LeIT7/KUOk8S1l3AiWsIRrn8K8a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/phpHH/btso23LeIT7/KUOk8S1l3AiWsIRrn8K8a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FphpHH%2Fbtso23LeIT7%2FKUOk8S1l3AiWsIRrn8K8a0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1518&quot; height=&quot;425&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;425&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. ScreenSpace 데칼 레이어를 활성화 상태로 MSAA를 활성화하면 DepthNormalPrepass를 강제로 사용한다. 이 부분은 유니티 버그로 추측된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 1번 상태에서 Depth Priming Mode가 활성화되면 DepthNormalPrepass에 아무것도 렌더링되지 않아 불투명 오브젝트가 렌더링되지 않는다. (GameView에서만 문제가 발생하고 SceneView는 정상 렌더링된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 데칼이 아닌 SSAO로 동일한 조건을 만들면 이슈가 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <category>Rendering Layer</category>
      <category>unity</category>
      <category>URP</category>
      <category>데칼 레이어</category>
      <category>유니티 버그</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/153</guid>
      <comments>https://darkcatgame.tistory.com/153#entry153comment</comments>
      <pubDate>Tue, 25 Jul 2023 23:36:41 +0900</pubDate>
    </item>
    <item>
      <title>[번역] Arm Mali GPU Training Series Ep 2.6 : Shader best practices</title>
      <link>https://darkcatgame.tistory.com/152</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;원문&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/KCL9xiN_UbM&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://youtu.be/KCL9xiN_UbM&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=KCL9xiN_UbM&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/chbgox/hyTo1SA3GD/iB5wmcfmkpBI0v6ESW0uck/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;Arm Mali GPU Training Series Ep 2.6 : Shaders best practices&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/KCL9xiN_UbM&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mali GPU에서 더 나은 성능을 낼 수 있는 쉐이더 프로그래밍 Best Practices 소개&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Use lower precision - 낮은 정밀도 사용&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PHOjt/btsoEqhHCsh/ABHQ0kjkfzRcgvfgnnF141/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PHOjt/btsoEqhHCsh/ABHQ0kjkfzRcgvfgnnF141/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PHOjt/btsoEqhHCsh/ABHQ0kjkfzRcgvfgnnF141/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPHOjt%2FbtsoEqhHCsh%2FABHQ0kjkfzRcgvfgnnF141%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1247&quot; height=&quot;700&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mediump 또는 Vulkan Relaxed Precision을 사용하는 것을 권장.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(중간 정밀도를 의미하며 &lt;b&gt;HLSL에서 half&lt;/b&gt;가 mediump에 가장 가까운 자료형.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 정밀도는 16비트 자료형 및 정수 유형에 매핑되고 사용은 에너지 효율이 더 좋고 더 작은 레지스터(메모리)를 사용하고 연산이 빠르다, Mail GPU에서 16비트 Vector2 2개를 연산하는 속도와 32비트 연산과 동일한 속도로 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(GPU API마다 특정 조건에서 16비트가 더 낮은 퍼포먼스가 나오는 경우도 있음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 정밀도 자료형의 단점은 동적 범위와 정밀도에서 제한이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(최대 &amp;amp; 최소 값과 소수점 길이 제한이 있다는 뜻)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 월드 공간에서 위치나 거리를 계산하는 공간 연산, 그림자 맵핑 &amp;amp; 뎁스 맵핑과 같은 샘플링 &amp;amp; 텍스처 샘플링에 사용되는 UV 좌표에는 32비트 보간자를 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(추가로 _Time변수와 연산하는 변수에도 32비트 권장)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형변환 주의, 이것은 공짜가 아니다.(퍼포먼스 공짜가 아니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 부동소수점 연산 주의 (부동소수점 = float, half와 같은 자료형)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wPEYC/btsoIN4GwMw/fd9ulzXI7DOtBuvxWIKMN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wPEYC/btsoIN4GwMw/fd9ulzXI7DOtBuvxWIKMN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wPEYC/btsoIN4GwMw/fd9ulzXI7DOtBuvxWIKMN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwPEYC%2FbtsoIN4GwMw%2Ffd9ulzXI7DOtBuvxWIKMN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;694&quot; height=&quot;300&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 코드에서 2번 코드로 곱연산을 1번으로 줄여 최적화한것 처럼 보이겠지만, 부동소수점을 사용하고 있다면 컴파일러가 에러가 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1257&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W6KEL/btsoTmRWKya/4hFFUUhuLLAmtMlq350mz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W6KEL/btsoTmRWKya/4hFFUUhuLLAmtMlq350mz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W6KEL/btsoTmRWKya/4hFFUUhuLLAmtMlq350mz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW6KEL%2FbtsoTmRWKya%2F4hFFUUhuLLAmtMlq350mz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;760&quot; height=&quot;428&quot; data-origin-width=&quot;1257&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 위와 같이 a와 b에 부동 소수점의 최대값으로 세팅한다면, 우측 최적화 시도한 코드에서 덧셈이 먼저 진행되어 부동소수점이 초과되는 이슈가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이러한 경우 GPU마다 다르겠지만 대체로 큰 퍼포먼스 저하와 무한대 값이 들어가 픽셀 이상 이슈 발생)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 내장 함수를 사용하자&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1243&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be9O2e/btsoQsZUIYP/D0TvH38yHLSwzBzvnwBe0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be9O2e/btsoQsZUIYP/D0TvH38yHLSwzBzvnwBe0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be9O2e/btsoQsZUIYP/D0TvH38yHLSwzBzvnwBe0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe9O2e%2FbtsoQsZUIYP%2FD0TvH38yHLSwzBzvnwBe0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1243&quot; height=&quot;682&quot; data-origin-width=&quot;1243&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더 언어에는 내장 함수 및 대규모 내장 함수 라이브러리가 있다.&amp;nbsp;&lt;br /&gt;(ESSL = OpenGLES Shading Language, ESSL 뿐만 아니라 HLSL와 같이 각 쉐이더 언어마다 비슷하게 내장 함수 있음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더 언어에서 제공하는 내장 함수를 사용하는 것이 직접 구현하는 것보다 거의 항상 퍼포먼스적으로 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 하드웨어에 의해 지원되거나 특정 GPU 버전에 대한 핸드 튜닝했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dO75Uu/btsoNWfWP0B/RY3Mk8hgT9E8cKGffToctK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dO75Uu/btsoNWfWP0B/RY3Mk8hgT9E8cKGffToctK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dO75Uu/btsoNWfWP0B/RY3Mk8hgT9E8cKGffToctK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdO75Uu%2FbtsoNWfWP0B%2FRY3Mk8hgT9E8cKGffToctK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;436&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 고정밀도 인자(inputs)를 사용하는 함수는 비싸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(void function(float a) &amp;lt;- float a을 의미)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내장 함수 중에 같은 함수라도 중간 정밀도를 사용하는 함수가 더 빠르기 때문에 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내장 함수들은 퍼포먼스가 무거우니 피하도록 노력해라 - 삼각함수 계열(sin, cos, tan), Atomics, textureGrad&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 유니폼 계산은 CPU에서 처리&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lOL0d/btsoIMLuBxP/LNrQfw41DrzegP5jfmqNr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lOL0d/btsoIMLuBxP/LNrQfw41DrzegP5jfmqNr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lOL0d/btsoIMLuBxP/LNrQfw41DrzegP5jfmqNr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlOL0d%2FbtsoIMLuBxP%2FLNrQfw41DrzegP5jfmqNr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;788&quot; height=&quot;440&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니폼 연산은 드로우 콜이나 컴퓨트 디스패치의 모든 스레드에 대해 동일한 결과를 내는 연산을 의미한다. 이러한 경우 CPU에서 연산을 처리하는 것을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mali GPU에서 이러한 연산에 대해서 어느정도 최적화 할 수 있지만, 완전 최적화할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(예 : 오브젝트 Pivot과 카메라와 거리 계산은 모든 픽셀의 결과 값이 동일하기 때문에 CPU에서 처리하는 것이 효율적임)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 쉐이더 베리에이션 관련&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1235&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIjvwG/btsoGhrpWgQ/tkEcHx8Yf1ohOrjjNMkNC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIjvwG/btsoGhrpWgQ/tkEcHx8Yf1ohOrjjNMkNC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIjvwG/btsoGhrpWgQ/tkEcHx8Yf1ohOrjjNMkNC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIjvwG%2FbtsoGhrpWgQ%2FtkEcHx8Yf1ohOrjjNMkNC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1235&quot; height=&quot;693&quot; data-origin-width=&quot;1235&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉐이더 사용 용도에 맞게 Specailization(특화)해라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(용도 외에 쓸데없는 기능 빼라)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 우버 쉐이더 1개보다 특정 용도로 특화된 쉐이더를 사용하는 것을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특화 쉐이더는 컴파일 시간 단축에 효과적이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 런타임 쉐이더 변경은 부하가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특화 방식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- if-else 조건문을 특수화하면 쉐이더에서 중복 연산을 방지할 수 있다.&amp;nbsp;&lt;br /&gt;&amp;nbsp; (if else쓰지 말라는 뜻)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- for문 같은 반복문은 unroll 구문과 같은 고정 반복문으로 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 유니폼 함수에서 절대 변동되지 않는 상수를 반환하게 하면 컴파일 시간 단&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 단인 일스턴스를 드로우 할때 인스턴싱을 사용하지 않는 것이 좋음. &lt;br /&gt;&amp;nbsp; &amp;nbsp;(예 : GPU Instancing)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1229&quot; data-origin-height=&quot;685&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3CzqV/btsoKYE9T4E/XNyQmxjrID0pMzVfJvT0K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3CzqV/btsoKYE9T4E/XNyQmxjrID0pMzVfJvT0K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3CzqV/btsoKYE9T4E/XNyQmxjrID0pMzVfJvT0K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3CzqV%2FbtsoKYE9T4E%2FXNyQmxjrID0pMzVfJvT0K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;370&quot; data-origin-width=&quot;1229&quot; data-origin-height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론 = 단일 기능이 있는 특화 쉐이더와 여러 기능이 포함된 쉐이더를 만들어 관리가 용의하게 하는 것은 프로젝트 및 개발자에 맞게 균형을 잡고 개발하는 것이 중요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 브렌치 구문 주의점 (if-else)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SHbrk/btsoSj2h88m/vqWR1IA7Qg3r3UPB7oNEa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SHbrk/btsoSj2h88m/vqWR1IA7Qg3r3UPB7oNEa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SHbrk/btsoSj2h88m/vqWR1IA7Qg3r3UPB7oNEa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSHbrk%2FbtsoSj2h88m%2FvqWR1IA7Qg3r3UPB7oNEa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;384&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 세대 Mali GPU 아키텍처인 Utgard/Midgard에서는 분기문을 사용하면 SIMD 처리 방식에서 연산 그룹이 더 잘게 분할되어 비효율적으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신 세대 Mali GPU 아키텍처인 Bifrost / Valhall에서는 분기문은 상대적으로 가볍다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- SIMD : Single Instruction, Multiple Data의 약자로 한번에 여러 데이터를 동시에 연산하는 처리 방식의 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Utgard/Midgard : Utgard는 OpenGLES 2.0까지 지원, Midgard는 OpenGLES 3.1, Vulkan 1.0까지 지원.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Bifrost / Valhall : 가장 최신 Mali 아키텍처로 OpenGLES 3.2, Vulkan 1.1 이상 지원&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약 : 최신 Mali GPU에서는 분기문이 더 가볍다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIO7W4/btsoKXMOq6I/225DzMkVgoO1w8KdhAW0s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIO7W4/btsoKXMOq6I/225DzMkVgoO1w8KdhAW0s0/img.png&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;685&quot; data-is-animation=&quot;false&quot; width=&quot;796&quot; height=&quot;436&quot; style=&quot;width: 49.6218%; margin-right: 10px;&quot; data-widthpercent=&quot;50.21&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIO7W4/btsoKXMOq6I/225DzMkVgoO1w8KdhAW0s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIO7W4%2FbtsoKXMOq6I%2F225DzMkVgoO1w8KdhAW0s0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1250&quot; height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cztTDo/btsoQsFFMO5/2v8Tk8ElwLyovpooWz5KR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cztTDo/btsoQsFFMO5/2v8Tk8ElwLyovpooWz5KR1/img.png&quot; data-origin-width=&quot;1247&quot; data-origin-height=&quot;689&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.2154%;&quot; data-widthpercent=&quot;49.79&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cztTDo/btsoQsFFMO5/2v8Tk8ElwLyovpooWz5KR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcztTDo%2FbtsoQsFFMO5%2F2v8Tk8ElwLyovpooWz5KR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1247&quot; height=&quot;689&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일정 거리 이상의 픽셀에서 라이팅 연산을 하지 않는 조건문을 예시로 모든 라이팅 연산이 진행되지 않을 경우 5 사이클이지만, 라이팅 연산이 모두 진행하면 13사이클이 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 조건문을 제거하여 항상 라이팅 연산을 진행하게 할 경우, 최소 사이클은 조금 늘어나지만, 최대 사이클은 약 2배 감소되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 위 예시와 같은 경우 조건문이 없는게 더 나은 상황이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;685&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGLkhX/btsoTm5w1mL/TIErjWkmqLHEjgkmKSon2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGLkhX/btsoTm5w1mL/TIErjWkmqLHEjgkmKSon2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGLkhX/btsoTm5w1mL/TIErjWkmqLHEjgkmKSon2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGLkhX%2FbtsoTm5w1mL%2FTIErjWkmqLHEjgkmKSon2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;377&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문은 상황에 따라 좋을 수도 나쁠 수 도있으며 조건문 내부 코드는 심플할 수록 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;7. Vertex inputs 변수 패킹&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ehG89v/btsoRjuVIrg/QtBmQn9iOcO6UwpIq2aqyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ehG89v/btsoRjuVIrg/QtBmQn9iOcO6UwpIq2aqyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ehG89v/btsoRjuVIrg/QtBmQn9iOcO6UwpIq2aqyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FehG89v%2FbtsoRjuVIrg%2FQtBmQn9iOcO6UwpIq2aqyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1227&quot; height=&quot;688&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버텍스 쉐이더에서 Vector4를 로드하는 것이 Vector2 2개를 로드하는 것보다 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Vertex 쉐이더에서 Input &amp;amp; Output 구조체를 의미하는 것으로 추측)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레그먼트 쉐이더에서 단일 Vector4 보간하는 것이 Vector3 + float을 보간하는 것보다 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(? Interpolator-보간이 무엇을 의미하는지 모름)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;8. ZS Test 관련 주의점 (Z Buffer &amp;amp; Stencil Test)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1239&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qaOVf/btsoQLrHJl1/koM0FWuMeBkWvchh92zkJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qaOVf/btsoQLrHJl1/koM0FWuMeBkWvchh92zkJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qaOVf/btsoQLrHJl1/koM0FWuMeBkWvchh92zkJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqaOVf%2FbtsoQLrHJl1%2FkoM0FWuMeBkWvchh92zkJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1239&quot; height=&quot;690&quot; data-origin-width=&quot;1239&quot; data-origin-height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Early ZS 테스트 혹은 숨겨진 표면 제거를 이용해 가능한 많은 중복 오버드로를 제거하는 것이 성능 향상에 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(번역 그대로 작성, 정확한 내용 정리 후 추후 다시 정리)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부&amp;nbsp;셰이더&amp;nbsp;기능은&amp;nbsp;상태가&amp;nbsp;셰이더가&amp;nbsp;실행될&amp;nbsp;때까지&amp;nbsp;알려지지&amp;nbsp;않기&amp;nbsp;때문에&amp;nbsp;이러한&amp;nbsp;최적화를&amp;nbsp;비활성화할&amp;nbsp;수&amp;nbsp;있습니다. &lt;br /&gt;discard&amp;nbsp;문을&amp;nbsp;사용하면&amp;nbsp;조기&amp;nbsp;ZS&amp;nbsp;테스팅은&amp;nbsp;가능하지만,&amp;nbsp;조기&amp;nbsp;ZS&amp;nbsp;업데이트와&amp;nbsp;숨겨진&amp;nbsp;표면&amp;nbsp;제거는&amp;nbsp;비활성화됩니다. &lt;br /&gt;프로그래마틱&amp;nbsp;깊이&amp;nbsp;쓰기를&amp;nbsp;사용하면&amp;nbsp;모든&amp;nbsp;조기&amp;nbsp;ZS와&amp;nbsp;숨겨진&amp;nbsp;표면&amp;nbsp;제거가&amp;nbsp;비활성화됩니다. &lt;br /&gt;프레임&amp;nbsp;버퍼에서&amp;nbsp;프로그래마틱&amp;nbsp;깊이&amp;nbsp;읽기를&amp;nbsp;사용하면&amp;nbsp;조기&amp;nbsp;ZS&amp;nbsp;테스팅은&amp;nbsp;가능하지만,&amp;nbsp;조기&amp;nbsp;ZS&amp;nbsp;업데이트와&amp;nbsp;숨겨진&amp;nbsp;표면&amp;nbsp;제거는&amp;nbsp;비활성화됩니다. &lt;br /&gt;프레임&amp;nbsp;버퍼&amp;nbsp;또는&amp;nbsp;픽셀&amp;nbsp;로컬&amp;nbsp;스토리지&amp;nbsp;구조에서&amp;nbsp;프로그래마틱&amp;nbsp;색상&amp;nbsp;읽기를&amp;nbsp;사용하면&amp;nbsp;숨겨진&amp;nbsp;표면&amp;nbsp;제거가&amp;nbsp;비활성화됩니다. &lt;br /&gt;이러한&amp;nbsp;조기&amp;nbsp;ZS&amp;nbsp;작업을&amp;nbsp;수행하는&amp;nbsp;콘텐츠의&amp;nbsp;경우,&amp;nbsp;업데이트가&amp;nbsp;필요하지&amp;nbsp;않은&amp;nbsp;경우&amp;nbsp;깊이&amp;nbsp;테스팅을&amp;nbsp;비활성화하거나&amp;nbsp;깊이&amp;nbsp;쓰기를&amp;nbsp;마스킹하는&amp;nbsp;것이&amp;nbsp;도움이&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;이는&amp;nbsp;성능을&amp;nbsp;향상시킬&amp;nbsp;수&amp;nbsp;있습니다. &lt;br /&gt;또한&amp;nbsp;셰이더에&amp;nbsp;layout&amp;nbsp;early_fragment_tests를&amp;nbsp;추가하는&amp;nbsp;것이&amp;nbsp;가능하며,&amp;nbsp;이는&amp;nbsp;조기&amp;nbsp;테스팅을&amp;nbsp;사용하는&amp;nbsp;것이&amp;nbsp;안전하다는&amp;nbsp;것을&amp;nbsp;컴파일러에&amp;nbsp;알려주는&amp;nbsp;역할을&amp;nbsp;합니다.&amp;nbsp;이는&amp;nbsp;셰이더가&amp;nbsp;위에서&amp;nbsp;언급한&amp;nbsp;작업&amp;nbsp;중&amp;nbsp;하나를&amp;nbsp;사용하더라도&amp;nbsp;적용됩니다. &lt;br /&gt;이&amp;nbsp;내용은&amp;nbsp;그래픽스&amp;nbsp;렌더링에서&amp;nbsp;중요한&amp;nbsp;최적화&amp;nbsp;기법&amp;nbsp;중&amp;nbsp;하나인&amp;nbsp;조기&amp;nbsp;ZS&amp;nbsp;테스팅&amp;nbsp;또는&amp;nbsp;숨겨진&amp;nbsp;표면&amp;nbsp;제거에&amp;nbsp;대한&amp;nbsp;설명입니다.&amp;nbsp;이를&amp;nbsp;통해&amp;nbsp;불필요한&amp;nbsp;렌더링&amp;nbsp;작업을&amp;nbsp;최소화하고&amp;nbsp;성능을&amp;nbsp;향상시킬&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;9. Compute Shader 개발 고려 사항(가이드)&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/liq0A/btsoQKGonKQ/GLshYda16KnZAXmNnlUARk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/liq0A/btsoQKGonKQ/GLshYda16KnZAXmNnlUARk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/liq0A/btsoQKGonKQ/GLshYda16KnZAXmNnlUARk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fliq0A%2FbtsoQKGonKQ%2FGLshYda16KnZAXmNnlUARk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1234&quot; height=&quot;695&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;695&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드 그룹 사이즈는 적당하게 세팅해야하며 보편적인 사이즈는 64다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적 쓰레드 그룹 사이즈는 GPU마다 다르기 때문에 게임 최초 실행시 벤치마크를 통해 적합한 크기를 찾는게 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드 공유 메모리는 쓰레드간에 데이터를 공유하는 역할을 하는데 일부 GPU에서는 성능을 높이기 위해서 메인 메모리에서 전용 RAM으로 데이터를 복사한다. 하지만 Mali GPU는 이에 해당되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 메모리에서 쓰레드 로컬 메모리로 복사하면 캐시가 오염되기 떄문에 실제 성능이 느려진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 알고리즘 데이터 공유에만 작업 그룹 로컬 메모리를 사용해라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Mali GPU에서 쓰레드 공유 메모리 사용시 성능 하락한다는 의미)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/TA</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/152</guid>
      <comments>https://darkcatgame.tistory.com/152#entry152comment</comments>
      <pubDate>Mon, 24 Jul 2023 23:09:31 +0900</pubDate>
    </item>
    <item>
      <title>URP ScreenSpaceDecal Shader</title>
      <link>https://darkcatgame.tistory.com/151</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4XqHU/btsn0DoEyWk/JhPtWaUPcrm6gEwYPiohK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4XqHU/btsn0DoEyWk/JhPtWaUPcrm6gEwYPiohK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4XqHU/btsn0DoEyWk/JhPtWaUPcrm6gEwYPiohK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4XqHU%2Fbtsn0DoEyWk%2FJhPtWaUPcrm6gEwYPiohK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1462&quot; height=&quot;788&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1689685549989&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shader &quot;CatDarkGame/ScreenSpaceDecal&quot;
{
    Properties
    { 
        _BaseColor(&quot;Color&quot;, Color) = (1, 1, 1, 1)
        _BaseMap(&quot;Texture&quot;, 2D) = &quot;white&quot; {}
    }

    SubShader
    {
        Tags { &quot;RenderType&quot; = &quot;Transparent&quot; &quot;Queue&quot; = &quot;Transparent-499&quot; &quot;DisableBatching&quot; = &quot;True&quot; }
        

        Pass
        {
            Name  &quot;ScreenSpaceDecal&quot;
            Tags {&quot;LightMode&quot; = &quot;SRPDefaultUnlit&quot;}

            Cull back
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha 

            HLSLPROGRAM
            
            #pragma target 4.5
            
            
            #pragma vertex vert 
            #pragma fragment frag

            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;
            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl&quot;


            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                float4 _BaseColor;
            CBUFFER_END

            TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);


            struct Attributes
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 positionSS : TEXCOORD1;
                float4 viewRayOS : TEXCOORD2; 
                float3 cameraPosOS : TEXCOORD3;
            }; 
            
            
            void GetObjectSpaceViewTransform(float3 positionVS, out float4 objectToViewDir, out float3 viewPositionOS)
            {
                float viewZ = positionVS.z;
                float4x4 viewToObjectMatrix = mul(UNITY_MATRIX_I_M, UNITY_MATRIX_I_V);
                objectToViewDir.xyz = mul((float3x3)viewToObjectMatrix, positionVS.xyz * -1).xyz;
                objectToViewDir.w = viewZ;
                viewPositionOS.xyz = mul(viewToObjectMatrix, float4(0.0f, 0.0f, 0.0f, 1.0f)).xyz; 
            }


            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;

                float4 positionOS = input.positionOS;
                float3 positionWS = TransformObjectToWorld(positionOS.xyz);
                float4 positionCS = TransformWorldToHClip(positionWS);
                float3 positionVS = TransformWorldToView(positionWS);
                float4 positionSS = ComputeScreenPos(positionCS);
               
                float4 objectToViewDir = 0;
                float3 viewPositionOS = 0;
                GetObjectSpaceViewTransform(positionVS, objectToViewDir, viewPositionOS);
                
                output.viewRayOS = objectToViewDir;
                output.cameraPosOS = viewPositionOS;
                output.positionSS = positionSS;
                output.positionCS = positionCS;
                output.uv = input.uv;
                return output;
            }

            float4 frag(Varyings input) : SV_Target
            {
                float sceneRawDepth = SampleSceneDepth(input.positionSS.xy / input.positionSS.w).r; 
                float sceneDepthVS = LinearEyeDepth(sceneRawDepth, _ZBufferParams);
                float3 viewRayOS = (input.viewRayOS.xyz / input.viewRayOS.w) * sceneDepthVS;
                float3 decalSpaceScenePos = input.cameraPosOS.xyz + viewRayOS.xyz;
                
                float3 absDecalSpaceScenePos = abs(decalSpaceScenePos);
                float decalClipAlpha = max(max(absDecalSpaceScenePos.x, absDecalSpaceScenePos.y), absDecalSpaceScenePos.z);
                clip(0.5f - decalClipAlpha);
               
                float2 decalSpaceUV = decalSpaceScenePos.xy + 0.5f;
                float2 uv = decalSpaceUV.xy * _BaseMap_ST.xy + _BaseMap_ST.zw;
                uv = frac(uv);
              
                float4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);
                float4 finalColor = texColor * _BaseColor;
                return finalColor;
            }
            
            ENDHLSL
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;구현 원리&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. View Space 기준 Object Transform 계산&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View Space, 즉 카메라가 오브젝트를 보는 기준으로 변형된 Transform를 다시 ObjectSpace로 변환합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;나중에 Fragment 쉐이더에서 깊이값과 Screen Position 기준으로 데칼 맵핑을 하기 위함입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689685682807&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void GetObjectSpaceViewTransform(float3 positionVS, out float4 objectToViewDir, out float3 viewPositionOS)
{
    float viewZ = positionVS.z;
    
    // 월드 공간 -&amp;gt; 뷰 공간 -&amp;gt; 오브젝트 공간으로 변환하기 위한 역행렬 계산
    float4x4 viewToObjectMatrix = mul(UNITY_MATRIX_I_M, UNITY_MATRIX_I_V);
    
    // View -&amp;gt; 오브젝트가 아닌 오브젝트 -&amp;gt; View 방향의 Transform으로 역행렬 계산
    objectToViewDir.xyz = mul((float3x3)viewToObjectMatrix, positionVS.xyz * -1).xyz;
    
    // w에는 뷰 공간 깊이값 정보 저장
    objectToViewDir.w = viewZ;
    
    // 카메라 Transform를 오브젝트 공간 변환
    viewPositionOS.xyz = mul(viewToObjectMatrix, float4(0.0f, 0.0f, 0.0f, 1.0f)).xyz; 
}

Varyings vert(Attributes input)
{
    Varyings output = (Varyings)0;

    float4 positionOS = input.positionOS;
    float3 positionWS = TransformObjectToWorld(positionOS.xyz);
    float3 positionVS = TransformWorldToView(positionWS);
    float4 objectToViewDir = 0;
    float3 viewPositionOS = 0;
    GetObjectSpaceViewTransform(positionVS, objectToViewDir, viewPositionOS);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 데칼 맵핑 포지션 계산&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 카메라 공간 기준, 거리 값을 이용해서 화면 상의 정확한 위치에 데칼 맵핑 포지션을 계산합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689687259145&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float4 frag(Varyings input) : SV_Target
{
	// SceneDepth 샘플링, ScreenPos XY에 W를 나눠서 원근 기준 값을 평면 기준으로 변환 
    float sceneRawDepth = SampleSceneDepth(input.positionSS.xy / input.positionSS.w).r; 
    
    // 카메라에서 Depth픽셀까지 거리 값 계산
    float sceneDepthVS = LinearEyeDepth(sceneRawDepth, _ZBufferParams);
    
    // xyz/w 계산으로 오브젝트 스페이스의 포지션 값을 원점으로 변환, 다음 depth값을 곱하여 올바른 거리 위치로 좌표 계산
    float3 viewRayOS = (input.viewRayOS.xyz / input.viewRayOS.w) * sceneDepthVS;
    
    // 카메라 원점 기준으로 Offset처리하여 올바른 XY위치로 위치 변환
    float3 decalSpaceScenePos = input.cameraPosOS.xyz + viewRayOS.xyz;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명이 복잡하지만 예시 자료처럼 화면 기준으로 위치 값을 계산해서 카메라로 부터 뎁스 깊이 위치까지 레이저를 쏘는 흐름으로 이해하시면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;e44e72beb6fcba5eacebdd50cb34a022.gif&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beZ801/btsn8kVnzxl/W0I3JbhKWJ1KKijeuPRQ7K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beZ801/btsn8kVnzxl/W0I3JbhKWJ1KKijeuPRQ7K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beZ801/btsn8kVnzxl/W0I3JbhKWJ1KKijeuPRQ7K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/beZ801/btsn8kVnzxl/W0I3JbhKWJ1KKijeuPRQ7K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;263&quot; data-filename=&quot;e44e72beb6fcba5eacebdd50cb34a022.gif&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. AlphaClip으로 데칼 외부 픽셀 제거&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데칼은 Mesh가 겹쳐진 부분에 맵핑됩니다, 따라서 아래와 같이 실제 데칼 픽셀보다 큰 사이즈의 메쉬를 사용하게 되는데 실제 데칼 맵핑 외부 픽셀은 Clip함수로 제거해야합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JGBef/btsobcPPAc3/KRFlEQ7X7NnPAXbfcotIBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JGBef/btsobcPPAc3/KRFlEQ7X7NnPAXbfcotIBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JGBef/btsobcPPAc3/KRFlEQ7X7NnPAXbfcotIBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJGBef%2FbtsobcPPAc3%2FKRFlEQ7X7NnPAXbfcotIBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;391&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1689687742351&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float3 absDecalSpaceScenePos = abs(decalSpaceScenePos);
float decalClipAlpha = max(max(absDecalSpaceScenePos.x, absDecalSpaceScenePos.y), absDecalSpaceScenePos.z);
clip(0.5f - decalClipAlpha);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 계산했던 데칼 포지션은 오브젝트 공간 기준으로 계산되었기 때문에 UV값이 중심에서 밖으로 커집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해서 메쉬 사이즈 밖으로 나가는 픽셀을 제거하면 되기 때문에 위와 같이 xyz의 최대 값 기준으로 일정 값이상 커지는 픽셀을 clip처리 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(추후 쉽게 설명할 수 있게 되면 글 업데이트 예정)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMXw0c/btsn0GeFH88/rHK2Exo6HbZefHv8QLchu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMXw0c/btsn0GeFH88/rHK2Exo6HbZefHv8QLchu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMXw0c/btsn0GeFH88/rHK2Exo6HbZefHv8QLchu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMXw0c%2Fbtsn0GeFH88%2FrHK2Exo6HbZefHv8QLchu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1462&quot; height=&quot;772&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/ColinLeung-NiloCat/UnityURPUnlitScreenSpaceDecalShader/blob/master/URP_NiloCatExtension_ScreenSpaceDecal_Unlit.shader&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/ColinLeung-NiloCat/UnityURPUnlitScreenSpaceDecalShader/blob/master/URP_NiloCatExtension_ScreenSpaceDecal_Unlit.shader&lt;/a&gt;&lt;/p&gt;</description>
      <category>Unity/Shader &amp;amp; VFX</category>
      <category>Decal</category>
      <category>URP 데칼</category>
      <category>데칼</category>
      <category>데칼 쉐이더</category>
      <category>유니티</category>
      <category>유니티 데칼</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/151</guid>
      <comments>https://darkcatgame.tistory.com/151#entry151comment</comments>
      <pubDate>Tue, 18 Jul 2023 22:52:17 +0900</pubDate>
    </item>
    <item>
      <title>LinearEyeDepth와 Linear01Depth</title>
      <link>https://darkcatgame.tistory.com/150</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LinearEyeDepth&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 카메라에서 픽셀까지의 거리를 선형 스케일로 반환합니다. 이 값은 월드 공간에서의 실제 거리를 나타내며, 일반적으로 뷰 공간에서의 Z 좌표로 해석됩니다. 이 함수는 깊이 정보를 실제 거리 값으로 사용해야 하는 경우에 유용합니다. 예를 들어, 포그(fog) 효과를 구현하거나, 깊이에 따라 물체의 색상을 변경하는 등의 작업에 사용될 수 있습니다. &lt;br /&gt;&lt;br /&gt;Linear01Depth&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 카메라에서 픽셀까지의 거리를 0과 1 사이의 값으로 정규화하여 반환합니다. 이 값은 카메라의 가까운 클리핑 평면(near clipping plane)에서 멀리 있는 클리핑 평면(far clipping plane)까지의 거리를 0과 1 사이로 나타냅니다. 이 함수는 깊이 정보를 비율이나 백분율로 해석해야 하는 경우에 유용합니다. 예를 들어, 깊이에 따라 투명도를 변경하거나, 포스트 프로세싱 효과를 적용하는 등의 작업에 사용될 수 있습니다.&lt;/p&gt;</description>
      <category>Unity/Shader &amp;amp; VFX</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/150</guid>
      <comments>https://darkcatgame.tistory.com/150#entry150comment</comments>
      <pubDate>Thu, 13 Jul 2023 21:27:21 +0900</pubDate>
    </item>
    <item>
      <title>Unity Triplanar Shader 분석 &amp;amp; StaticWorldPosition 응용</title>
      <link>https://darkcatgame.tistory.com/148</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Triplanar 기본 구현&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월드 포지션 2개 축을 조합해서 UV 좌표로 활용 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688995600700&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Varyings vert(Attributes input)
{
    Varyings output = (Varyings)0;

    float4 positionOS = input.positionOS;
    float3 positionWS = TransformObjectToWorld(positionOS.xyz);
    float4 positionCS = TransformWorldToHClip(positionWS);
    ...
    
    
float4 frag(Varyings input) : SV_Target
{
    float3 positionWS = input.positionWS;
    return float4(positionWS.xy + 0.5f, 0,0);
    return float4(positionWS.zx + 0.5f, 0,0);
    return float4(positionWS.zy + 0.5f, 0,0);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zN32P/btsm9CipVhZ/KQr8FpyduewNPVIgBuXD4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zN32P/btsm9CipVhZ/KQr8FpyduewNPVIgBuXD4K/img.png&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;594&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.9404%; margin-right: 10px;&quot; data-widthpercent=&quot;32.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zN32P/btsm9CipVhZ/KQr8FpyduewNPVIgBuXD4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzN32P%2Fbtsm9CipVhZ%2FKQr8FpyduewNPVIgBuXD4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;633&quot; height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XgBiU/btsm8Kn0eES/ZcM397kpqJGXRhzJWmHMvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XgBiU/btsm8Kn0eES/ZcM397kpqJGXRhzJWmHMvk/img.png&quot; data-origin-width=&quot;556&quot; data-origin-height=&quot;531&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.3836%; margin-right: 10px;&quot; data-widthpercent=&quot;32.13&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XgBiU/btsm8Kn0eES/ZcM397kpqJGXRhzJWmHMvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXgBiU%2Fbtsm8Kn0eES%2FZcM397kpqJGXRhzJWmHMvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;556&quot; height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/67SJR/btsm5sBc9sJ/klqVwnkRYQfsjLcKO1NZ6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/67SJR/btsm5sBc9sJ/klqVwnkRYQfsjLcKO1NZ6k/img.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;534&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.3505%;&quot; data-widthpercent=&quot;35.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/67SJR/btsm5sBc9sJ/klqVwnkRYQfsjLcKO1NZ6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F67SJR%2Fbtsm5sBc9sJ%2FklqVwnkRYQfsjLcKO1NZ6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;534&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;positionWS.XY,&amp;nbsp; &amp;nbsp; positionWS.ZX,&amp;nbsp; &amp;nbsp; positionWS.ZY&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 월드 노말 벡터를 픽셀 쉐이더로 확인하면 아래와 같이 각 방향 별로 마스킹 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688995864440&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Varyings vert(Attributes input)
{
    Varyings output = (Varyings)0;

    float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
    ...
    
    
float4 frag(Varyings input) : SV_Target
{
    float3 normalWS = NormalizeNormalPerPixel(input.normalWS);
    return float4(normalWS, 0);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T8G4Z/btsm831Gbg7/acZmrCZ4XeKgaqw4l7eHA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T8G4Z/btsm831Gbg7/acZmrCZ4XeKgaqw4l7eHA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T8G4Z/btsm831Gbg7/acZmrCZ4XeKgaqw4l7eHA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT8G4Z%2Fbtsm831Gbg7%2FacZmrCZ4XeKgaqw4l7eHA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;479&quot; height=&quot;424&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월드 포지션 기반 UV와 월드 노말 각 축을 곱하면 아래와 같이 깔끔하게 UV 좌표 계산 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688996225070&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; float4 frag(Varyings input) : SV_Target
{
    float3 positionWS = input.positionWS;
    float3 normalWS = NormalizeNormalPerPixel(input.normalWS);

    return float4((positionWS.xy + 0.5f) * normalWS.z, 0,0);
    return float4((positionWS.xz + 0.5f) * normalWS.y, 0,0);
    return float4((positionWS.zy + 0.5f) * normalWS.x, 0,0);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCjjbm/btsnak9GKXj/rOeB63wkXw4794n3BOY5xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCjjbm/btsnak9GKXj/rOeB63wkXw4794n3BOY5xK/img.png&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;582&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.6111%; margin-right: 10px;&quot; data-widthpercent=&quot;33.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCjjbm/btsnak9GKXj/rOeB63wkXw4794n3BOY5xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCjjbm%2Fbtsnak9GKXj%2FrOeB63wkXw4794n3BOY5xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HD354/btsm7ynQWWE/BlOPsAHWFKjbyhdiIPzUcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HD354/btsm7ynQWWE/BlOPsAHWFKjbyhdiIPzUcK/img.png&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;538&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.6379%; margin-right: 10px;&quot; data-widthpercent=&quot;33.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HD354/btsm7ynQWWE/BlOPsAHWFKjbyhdiIPzUcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHD354%2Fbtsm7ynQWWE%2FBlOPsAHWFKjbyhdiIPzUcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/soMgo/btsnaTqt456/Ri94yCDKyYbZkJ72Hkf6EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/soMgo/btsnaTqt456/Ri94yCDKyYbZkJ72Hkf6EK/img.png&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;535&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.4255%;&quot; data-widthpercent=&quot;33.2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/soMgo/btsnaTqt456/Ri94yCDKyYbZkJ72Hkf6EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsoMgo%2FbtsnaTqt456%2FRi94yCDKyYbZkJ72Hkf6EK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;574&quot; height=&quot;535&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;positionWS.XY,&amp;nbsp; &amp;nbsp; positionWS.ZX,&amp;nbsp; &amp;nbsp; positionWS.ZY&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 계산한 UV 좌표로 각각 3개 샘플링에 사용하면 Triplanar 기본형 완성입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688996805263&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float4 frag(Varyings input) : SV_Target
{
    float3 positionWS = input.positionWS + 0.5f;
    float3 normalWS = NormalizeNormalPerPixel(input.normalWS);

    float4 texColor_X = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, positionWS.zy * _BaseMap_ST.xy + _BaseMap_ST.zw);
    float4 texColor_Y = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, positionWS.zx * _BaseMap_ST.xy + _BaseMap_ST.zw);
    float4 texColor_Z = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, positionWS.xy * _BaseMap_ST.xy + _BaseMap_ST.zw);
    float4 triplanar = texColor_X * normalWS.x + texColor_Y * normalWS.y + texColor_Z * normalWS.z;

    float4 finalColor = triplanar;
    return finalColor;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vt6cM/btsnbPH7gBG/t6nclcPcQ792k5vWFNtmH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vt6cM/btsnbPH7gBG/t6nclcPcQ792k5vWFNtmH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vt6cM/btsnbPH7gBG/t6nclcPcQ792k5vWFNtmH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvt6cM%2FbtsnbPH7gBG%2Ft6nclcPcQ792k5vWFNtmH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;519&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아래와 같이 월드 노말 반대 편은 0 이하 값이 되기 때문에 월드 노말에 abs처리하여 음수를 양수로 변환합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zSpX0/btsm8QuLciu/xBohBR5OzNOzfiQHX2H6m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zSpX0/btsm8QuLciu/xBohBR5OzNOzfiQHX2H6m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zSpX0/btsm8QuLciu/xBohBR5OzNOzfiQHX2H6m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzSpX0%2Fbtsm8QuLciu%2FxBohBR5OzNOzfiQHX2H6m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;110&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba8yjj/btsm9g00Hu1/Xb74s0xWRLIlgvnilXBhsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba8yjj/btsm9g00Hu1/Xb74s0xWRLIlgvnilXBhsK/img.png&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;531&quot; data-is-animation=&quot;false&quot; width=&quot;455&quot; height=&quot;299&quot; style=&quot;width: 49.2861%; margin-right: 10px;&quot; data-widthpercent=&quot;49.87&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba8yjj/btsm9g00Hu1/Xb74s0xWRLIlgvnilXBhsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba8yjj%2Fbtsm9g00Hu1%2FXb74s0xWRLIlgvnilXBhsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;808&quot; height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6RdFj/btsm92gVyFv/7FU5N97unSs762J8xkaHsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6RdFj/btsm92gVyFv/7FU5N97unSs762J8xkaHsK/img.png&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;553&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.5511%;&quot; data-widthpercent=&quot;50.13&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6RdFj/btsm92gVyFv/7FU5N97unSs762J8xkaHsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6RdFj%2Fbtsm92gVyFv%2F7FU5N97unSs762J8xkaHsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;846&quot; height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 아래 Shader Graph의 Triplanar 코드를 참고하여 함수 형태로 만들고 기능을 개선합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.unity3d.com/Packages/com.unity.shadergraph@16.0/manual/Triplanar-Node.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.unity3d.com/Packages/com.unity.shadergraph@16.0/manual/Triplanar-Node.html&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1688997385861&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;float4 TriplanarSample(float3 positionWS, float3 normalWS, float blend, 
                        TEXTURE2D_PARAM(Texture_X, sampler_Texture_X), float4 tilingOffsetX,
                        TEXTURE2D_PARAM(Texture_Y, sampler_Texture_Y), float4 tilingOffsetY,
                        TEXTURE2D_PARAM(Texture_Z, sampler_Texture_Z), float4 tilingOffsetZ)
{
    float3 Node_Blend = pow(abs(normalWS), blend);
    Node_Blend /= dot(Node_Blend, 1.0);
    float scrollSpeed = 0;
    scrollSpeed = _Time.y;
    float4 texColor_X = SAMPLE_TEXTURE2D(Texture_X, sampler_Texture_X, positionWS.zy * tilingOffsetX.xy + (tilingOffsetX.zw * scrollSpeed));
    float4 texColor_Y = SAMPLE_TEXTURE2D(Texture_Y, sampler_Texture_Y, positionWS.zx * tilingOffsetY.xy + (tilingOffsetY.zw * scrollSpeed));
    float4 texColor_Z = SAMPLE_TEXTURE2D(Texture_Z, sampler_Texture_Z, positionWS.xy * tilingOffsetZ.xy + (tilingOffsetZ.zw * scrollSpeed));

    float4 triplanar = texColor_X * Node_Blend.x + texColor_Y * Node_Blend.y + texColor_Z * Node_Blend.z;
    return triplanar;
}


float4 frag(Varyings input) : SV_Target
{
    float3 positionWS = input.positionWS;
    float3 normalWS = NormalizeNormalPerPixel(input.normalWS);

    float4 triplanar = TriplanarSample(positionWS + 0.5f, normalWS, 1, 
                                        TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0),
                                        TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0),
                                        TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0));

    float4 finalColor = triplanar;
    return finalColor;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;XX1.gif&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oQfjS/btsnaM5Yrbu/gUTXBi29JfWbdjO8yJFPq1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oQfjS/btsnaM5Yrbu/gUTXBi29JfWbdjO8yJFPq1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oQfjS/btsnaM5Yrbu/gUTXBi29JfWbdjO8yJFPq1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/oQfjS/btsnaM5Yrbu/gUTXBi29JfWbdjO8yJFPq1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;401&quot; data-filename=&quot;XX1.gif&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Triplanar 월드 포지션 고정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Triplanar 기본은 구현되었지만 오브젝트 좌표가 움직여도 월드 포지션 UV는 변하지 않기 때문에 위 결과물 예시와 같이 텍스처 UV가 움직이지 않는 현상이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 좌표가 이동하지 않는 배경 오브젝트에는 문제 없지만, 캐릭터와 같이 움직이는 오브젝트에는 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이동행렬을 고정한 상태로 월드 포지션을 계산하여 Triplanar에 사용한다면 개선 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시는 이전 빌보드 구현 과정에서 Rotation Transform을 고정하는 부분이 있었는데, 해당 구현 부분을 응용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;A7.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by9poF/btsm7wXRuSL/OCJGoUEa14hnFi5qPqPl50/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by9poF/btsm7wXRuSL/OCJGoUEa14hnFi5qPqPl50/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by9poF/btsm7wXRuSL/OCJGoUEa14hnFi5qPqPl50/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/by9poF/btsm7wXRuSL/OCJGoUEa14hnFi5qPqPl50/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;329&quot; data-filename=&quot;A7.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://darkcatgame.tistory.com/137&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://darkcatgame.tistory.com/137&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688998071712&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void GetCustomModelMatrix(out float4x4 outMoveMatrix, out float4x4 outRotationMatrix, out float4x4 outScaleMatrix)
{
    float4x4 scaleMatrix;
    float4 sx = float4(UNITY_MATRIX_M._m00, UNITY_MATRIX_M._m10, UNITY_MATRIX_M._m20, 0);
    float4 sy = float4(UNITY_MATRIX_M._m01, UNITY_MATRIX_M._m11, UNITY_MATRIX_M._m21, 0);
    float4 sz = float4(UNITY_MATRIX_M._m02, UNITY_MATRIX_M._m12, UNITY_MATRIX_M._m22, 0);
    float scaleX = length(sx);
    float scaleY = length(sy);
    float scaleZ = length(sz);
    scaleMatrix[0] = float4(scaleX, 0, 0, 0);
    scaleMatrix[1] = float4(0, scaleY, 0, 0);
    scaleMatrix[2] = float4(0, 0, scaleZ, 0);
    scaleMatrix[3] = float4(0, 0, 0, 1);
    outScaleMatrix = scaleMatrix;

    float4x4 rotationMatrix;
    rotationMatrix[0] = float4(UNITY_MATRIX_M._m00 / scaleX, UNITY_MATRIX_M._m01 / scaleY, UNITY_MATRIX_M._m02 / scaleZ, 0);
    rotationMatrix[1] = float4(UNITY_MATRIX_M._m10 / scaleX, UNITY_MATRIX_M._m11 / scaleY, UNITY_MATRIX_M._m12 / scaleZ, 0);
    rotationMatrix[2] = float4(UNITY_MATRIX_M._m20 / scaleX, UNITY_MATRIX_M._m21 / scaleY, UNITY_MATRIX_M._m22 / scaleZ, 0);
    rotationMatrix[3] = float4(0, 0, 0, 1);
    outRotationMatrix = rotationMatrix;

    float4x4 moveMatrix;
    moveMatrix[0] = float4(1, 0, 0, UNITY_MATRIX_M._m03);
    moveMatrix[1] = float4(0, 1, 0, UNITY_MATRIX_M._m13);
    moveMatrix[2] = float4(0, 0, 1, UNITY_MATRIX_M._m23);
    moveMatrix[3] = float4(0, 0, 0, UNITY_MATRIX_M._m33);
    outMoveMatrix = moveMatrix;
}

float3 TransformObjectToWorld_StaticPosition(float3 positionOS)
{
    float4x4 scaleMatrix;
    float4x4 rotationMatrix;
    float4x4 moveMatrix;
    GetCustomModelMatrix(moveMatrix, rotationMatrix, scaleMatrix);
    float4x4 modelMatrix_Static = mul(mul(1, rotationMatrix), scaleMatrix);
    return mul(modelMatrix_Static, float4(positionOS.xyz, 1)).xyz;
}

Varyings vert(Attributes input)
{
    Varyings output = (Varyings)0;

    float4 positionOS = input.positionOS;
    float3 positionWS = TransformObjectToWorld(positionOS.xyz);
    float4 positionCS = TransformWorldToHClip(positionWS);

    float3 positionWS_Static = TransformObjectToWorld_StaticPosition(positionOS.xyz);

    float3 normalWS = TransformObjectToWorldNormal(input.normalOS);

    output.normalWS = normalWS;
    output.positionCS = positionCS;
    output.uv = input.uv;
    output.positionWS_Static = positionWS_Static.xyz;
    return output;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dekTUy/btsm9vDD6Vv/mJ6tmylsege5OIayCkG0Kk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dekTUy/btsm9vDD6Vv/mJ6tmylsege5OIayCkG0Kk/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;719&quot; data-origin-height=&quot;384&quot; data-filename=&quot;XX3.gif&quot; style=&quot;width: 55.364%; margin-right: 10px;&quot; data-widthpercent=&quot;56.02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dekTUy/btsm9vDD6Vv/mJ6tmylsege5OIayCkG0Kk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdekTUy%2Fbtsm9vDD6Vv%2FmJ6tmylsege5OIayCkG0Kk%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLnsdJ/btsm9fVicMb/yFKy4zcelABLyYD8bVcrik/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLnsdJ/btsm9fVicMb/yFKy4zcelABLyYD8bVcrik/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;353&quot; data-filename=&quot;VV3.gif&quot; style=&quot;width: 43.4733%;&quot; data-widthpercent=&quot;43.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLnsdJ/btsm9fVicMb/yFKy4zcelABLyYD8bVcrik/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLnsdJ%2Fbtsm9fVicMb%2FyFKy4zcelABLyYD8bVcrik%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;float4x4&amp;nbsp;modelMatrix_Static&amp;nbsp;=&amp;nbsp;mul(mul(1,&amp;nbsp;rotationMatrix),&amp;nbsp;scaleMatrix);&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌보드 자료에는 rotationMatrix를 고정했지만 이번엔 이동행렬을 고정하기 때문에 위와 같이 코드를 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1688998333493&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shader &quot;CatDarkGame/Triplanar&quot;
{
    Properties
    { 
        [MainTexture] _BaseMap(&quot;Texture&quot;, 2D) = &quot;white&quot; {}
        [MainColor] _BaseColor(&quot;Color&quot;, Color) = (1, 1, 1, 1)
    }

    SubShader
    {
        Tags { &quot;RenderType&quot; = &quot;Opaque&quot; &quot;Queue&quot;=&quot;Geometry&quot; &quot;RenderPipeline&quot; = &quot;UniversalPipeline&quot; }
        LOD 100

        Pass
        {
            Name  &quot;URPUnlit&quot;
            Tags {&quot;LightMode&quot; = &quot;SRPDefaultUnlit&quot;}

            HLSLPROGRAM
            #pragma target 4.5
            #pragma exclude_renderers gles d3d9
            
            #pragma vertex vert
            #pragma fragment frag

            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;


            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                float4 _BaseColor;
            CBUFFER_END

            TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);


            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 positionWS_Static : TEXCOORD1;
                float3 normalWS : TEXCOORD2;
            }; 
            

            void GetCustomModelMatrix(out float4x4 outMoveMatrix, out float4x4 outRotationMatrix, out float4x4 outScaleMatrix)
            {
                float4x4 scaleMatrix;
                float4 sx = float4(UNITY_MATRIX_M._m00, UNITY_MATRIX_M._m10, UNITY_MATRIX_M._m20, 0);
                float4 sy = float4(UNITY_MATRIX_M._m01, UNITY_MATRIX_M._m11, UNITY_MATRIX_M._m21, 0);
                float4 sz = float4(UNITY_MATRIX_M._m02, UNITY_MATRIX_M._m12, UNITY_MATRIX_M._m22, 0);
                float scaleX = length(sx);
                float scaleY = length(sy);
                float scaleZ = length(sz);
                scaleMatrix[0] = float4(scaleX, 0, 0, 0);
                scaleMatrix[1] = float4(0, scaleY, 0, 0);
                scaleMatrix[2] = float4(0, 0, scaleZ, 0);
                scaleMatrix[3] = float4(0, 0, 0, 1);
                outScaleMatrix = scaleMatrix;

                float4x4 rotationMatrix;
                rotationMatrix[0] = float4(UNITY_MATRIX_M._m00 / scaleX, UNITY_MATRIX_M._m01 / scaleY, UNITY_MATRIX_M._m02 / scaleZ, 0);
                rotationMatrix[1] = float4(UNITY_MATRIX_M._m10 / scaleX, UNITY_MATRIX_M._m11 / scaleY, UNITY_MATRIX_M._m12 / scaleZ, 0);
                rotationMatrix[2] = float4(UNITY_MATRIX_M._m20 / scaleX, UNITY_MATRIX_M._m21 / scaleY, UNITY_MATRIX_M._m22 / scaleZ, 0);
                rotationMatrix[3] = float4(0, 0, 0, 1);
                outRotationMatrix = rotationMatrix;

                float4x4 moveMatrix;
                moveMatrix[0] = float4(1, 0, 0, UNITY_MATRIX_M._m03);
                moveMatrix[1] = float4(0, 1, 0, UNITY_MATRIX_M._m13);
                moveMatrix[2] = float4(0, 0, 1, UNITY_MATRIX_M._m23);
                moveMatrix[3] = float4(0, 0, 0, UNITY_MATRIX_M._m33);
                outMoveMatrix = moveMatrix;
            }

            float3 TransformObjectToWorld_StaticPosition(float3 positionOS)
            {
                float4x4 scaleMatrix;
                float4x4 rotationMatrix;
                float4x4 moveMatrix;
                GetCustomModelMatrix(moveMatrix, rotationMatrix, scaleMatrix);
                float4x4 modelMatrix_Static = mul(mul(1, rotationMatrix), scaleMatrix);
                return mul(modelMatrix_Static, float4(positionOS.xyz, 1)).xyz;
            }

            float4 TriplanarSample(float3 positionWS, float3 normalWS, float blend, 
                                    TEXTURE2D_PARAM(Texture_X, sampler_Texture_X), float4 tilingOffsetX,
                                    TEXTURE2D_PARAM(Texture_Y, sampler_Texture_Y), float4 tilingOffsetY,
                                    TEXTURE2D_PARAM(Texture_Z, sampler_Texture_Z), float4 tilingOffsetZ)
            {
                float3 Node_Blend = pow(abs(normalWS), blend);
                Node_Blend /= dot(Node_Blend, 1.0);
                float scrollSpeed = 0;
                scrollSpeed = _Time.y;
                float4 texColor_X = SAMPLE_TEXTURE2D(Texture_X, sampler_Texture_X, positionWS.zy * tilingOffsetX.xy + (tilingOffsetX.zw * scrollSpeed));
                float4 texColor_Y = SAMPLE_TEXTURE2D(Texture_Y, sampler_Texture_Y, positionWS.zx * tilingOffsetY.xy + (tilingOffsetY.zw * scrollSpeed));
                float4 texColor_Z = SAMPLE_TEXTURE2D(Texture_Z, sampler_Texture_Z, positionWS.xy * tilingOffsetZ.xy + (tilingOffsetZ.zw * scrollSpeed));

                float4 triplanar = texColor_X * Node_Blend.x + texColor_Y * Node_Blend.y + texColor_Z * Node_Blend.z;
                return triplanar;
            }


            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;

                float4 positionOS = input.positionOS;
                float3 positionWS = TransformObjectToWorld(positionOS.xyz);
                float4 positionCS = TransformWorldToHClip(positionWS);

                float3 positionWS_Static = TransformObjectToWorld_StaticPosition(positionOS.xyz);
 
                float3 normalWS = TransformObjectToWorldNormal(input.normalOS);

                output.normalWS = normalWS;
                output.positionCS = positionCS;
                output.uv = input.uv;
                output.positionWS_Static = positionWS_Static.xyz;
                return output;
            }

            float4 frag(Varyings input) : SV_Target
            {
                float3 positionWS = input.positionWS_Static;
                float3 normalWS = NormalizeNormalPerPixel(input.normalWS);
             
                float4 triplanar = TriplanarSample(positionWS + 0.5f, normalWS, 1, 
                                                    TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0),
                                                    TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0),
                                                    TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0));
                
                float4 finalColor = triplanar;
                return finalColor;
            }
            
            ENDHLSL
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;worldPivot계산하는 방식&lt;/p&gt;
&lt;pre id=&quot;code_1689316630015&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Shader &quot;CatDarkGame/Triplanar&quot;
{
    Properties
    { 
        [MainTexture] _BaseMap(&quot;Texture&quot;, 2D) = &quot;white&quot; {}
        [MainColor] _BaseColor(&quot;Color&quot;, Color) = (1, 1, 1, 1)
    }

    SubShader
    {
        Tags { &quot;RenderType&quot; = &quot;Opaque&quot; &quot;Queue&quot;=&quot;Geometry&quot; &quot;RenderPipeline&quot; = &quot;UniversalPipeline&quot; }
        LOD 100

        Pass
        {
            Name  &quot;URPUnlit&quot;
            Tags {&quot;LightMode&quot; = &quot;SRPDefaultUnlit&quot;}

            HLSLPROGRAM
            #pragma target 4.5
            #pragma exclude_renderers gles d3d9
            
            #pragma vertex vert
            #pragma fragment frag

            #include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;


            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                float4 _BaseColor;
            CBUFFER_END

            TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);


            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 positionWS_Static : TEXCOORD1;
                float3 normalWS : TEXCOORD2;
            }; 
            

            float4 TriplanarSample(float3 positionWS, float3 normalWS, float blend, 
                                    TEXTURE2D_PARAM(Texture_X, sampler_Texture_X), float4 tilingOffsetX,
                                    TEXTURE2D_PARAM(Texture_Y, sampler_Texture_Y), float4 tilingOffsetY,
                                    TEXTURE2D_PARAM(Texture_Z, sampler_Texture_Z), float4 tilingOffsetZ)
            {
                float3 Node_Blend = pow(abs(normalWS), blend);
                Node_Blend /= dot(Node_Blend, 1.0);
                float scrollSpeed = 0;
                scrollSpeed = _Time.y;
                float4 texColor_X = SAMPLE_TEXTURE2D(Texture_X, sampler_Texture_X, positionWS.zy * tilingOffsetX.xy + (tilingOffsetX.zw * scrollSpeed));
                float4 texColor_Y = SAMPLE_TEXTURE2D(Texture_Y, sampler_Texture_Y, positionWS.zx * tilingOffsetY.xy + (tilingOffsetY.zw * scrollSpeed));
                float4 texColor_Z = SAMPLE_TEXTURE2D(Texture_Z, sampler_Texture_Z, positionWS.xy * tilingOffsetZ.xy + (tilingOffsetZ.zw * scrollSpeed));

                float4 triplanar = texColor_X * Node_Blend.x + texColor_Y * Node_Blend.y + texColor_Z * Node_Blend.z;
                return triplanar;
            }


            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;

                float4 positionOS = input.positionOS;
                float3 positionWS = TransformObjectToWorld(positionOS.xyz);
                float4 positionCS = TransformWorldToHClip(positionWS);
                float3 pivotWS = float3(unity_ObjectToWorld._m03,  unity_ObjectToWorld._m13,  unity_ObjectToWorld._m23);

                float3 normalWS = TransformObjectToWorldNormal(input.normalOS);

       
                output.normalWS = normalWS;
                output.positionCS = positionCS;
                output.uv = input.uv;
                output.positionWS_Static = positionWS.xyz - pivotWS;
                return output;
            }

            float4 frag(Varyings input) : SV_Target
            {
                float3 positionWS = input.positionWS_Static;
                float3 normalWS = NormalizeNormalPerPixel(input.normalWS);
             
                float4 triplanar = TriplanarSample(positionWS + 0.5f, normalWS, 1, 
                                                    TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0),
                                                    TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0),
                                                    TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap), float4(_BaseMap_ST.xy, 0, 0));
                
                float4 finalColor = triplanar;
                return finalColor;
            }
            
            ENDHLSL
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Unity/Shader &amp;amp; VFX</category>
      <category>triplanar</category>
      <category>Triplanar Static world position</category>
      <category>unity</category>
      <category>이동행렬</category>
      <author>CatDarkGame</author>
      <guid isPermaLink="true">https://darkcatgame.tistory.com/148</guid>
      <comments>https://darkcatgame.tistory.com/148#entry148comment</comments>
      <pubDate>Mon, 10 Jul 2023 23:26:26 +0900</pubDate>
    </item>
  </channel>
</rss>