<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>나에유의 개발이에유</title>
    <link>https://naeu.tistory.com/</link>
    <description>naeu 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 23 Sep 2026 03:08:58 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>NAEU</managingEditor>
    <image>
      <title>나에유의 개발이에유</title>
      <url>https://tistory1.daumcdn.net/tistory/7849762/attach/966c6112cbf146a59b7606382418cf2d</url>
      <link>https://naeu.tistory.com</link>
    </image>
    <item>
      <title>Depth 양은 알 수가 없어</title>
      <link>https://naeu.tistory.com/14</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ref. 아하렌 양은 알 수가 없어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 3일동안 있었던 버그를 잡은 짧은 회고록입니다&lt;br /&gt;렌더링 과정에서 depth test가 이루어지지 않아 그리는 순서대로 위에 그려지는 현상이 있었습니다&lt;/p&gt;
&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;856&quot; data-origin-height=&quot;751&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ6UwE/dJMcabDY3Du/UljL8Z6cbX4fuV9kzK1kTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ6UwE/dJMcabDY3Du/UljL8Z6cbX4fuV9kzK1kTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ6UwE/dJMcabDY3Du/UljL8Z6cbX4fuV9kzK1kTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ6UwE%2FdJMcabDY3Du%2FUljL8Z6cbX4fuV9kzK1kTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;751&quot; data-filename=&quot;ㅁ1.png&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;751&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;우선 view 혹은 projection의 matrix가 제대로 넘어가지지 않나 둘의 depth값을 확인해봤습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ㅁ2.png&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BmhVF/dJMcabDY3DX/tEN4F31aPKqjQLGtvP5yAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BmhVF/dJMcabDY3DX/tEN4F31aPKqjQLGtvP5yAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BmhVF/dJMcabDY3DX/tEN4F31aPKqjQLGtvP5yAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBmhVF%2FdJMcabDY3DX%2FtEN4F31aPKqjQLGtvP5yAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;896&quot; height=&quot;710&quot; data-filename=&quot;ㅁ2.png&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;depth색은 정상적으로 적용되었습니다(멀수록 어둡게)&lt;br /&gt;이때 알아낸 것은 현재 vector에 MeshRenderer를 담고 순차대로 draw를 진행하는데, 이 순차대고 먼저 그려진다는 사실입니다&lt;br /&gt;따라서 depth test 자체가 이루어지지 않고 있다는 것을 의심했습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;하지만 DepthStencilBuffer 생성도 통과하였고&lt;br /&gt;DepthStencilState도 정상적으로 생성되었습니다&lt;br /&gt;여기서 잠시 막히게 되었지만 결국 원인을 발견하였습니다&lt;/p&gt;
&lt;pre id=&quot;code_1777101768499&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DXGI_SWAP_CHAIN_DESC scd;
ZeroMemory(&amp;amp;scd, sizeof(scd));

scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; //버퍼 설정 중 색상 설정
scd.SampleDesc.Count = 4;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; //스왑체인 사용 방식
scd.BufferCount = 1; //백 버퍼 갯수
scd.OutputWindow = hwnd;
scd.Windowed = TRUE; //창모드&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_1777101795593&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;D3D11_TEXTURE2D_DESC depthTexDesc{};
depthTexDesc.Width = 800;
depthTexDesc.Height = 800;
depthTexDesc.MipLevels = 1;
depthTexDesc.ArraySize = 1;
depthTexDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthTexDesc.SampleDesc.Count = 1;
depthTexDesc.SampleDesc.Quality = 0;
depthTexDesc.Usage = D3D11_USAGE_DEFAULT;
depthTexDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;다음은 swapchain을 위한 desc와 depth stencil을 위한 desc입니다&lt;br /&gt;문제는 SampleDesc.Count에 있었습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Sample이란 픽셀의 색을 지정할때 부드럽게 하기 위한 방법입니다&lt;br /&gt;픽셀 하나에 대해 N개의 점을 찍어 도형이 해당 점 위를 덮는지 여부를 파악하고, 모든 점들의 평균값을 구하여 해당 픽셀에 적용합니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ㅁ3.png&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q66N4/dJMcabDY3D4/TfD53xnw3kjlz5m4r13qK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q66N4/dJMcabDY3D4/TfD53xnw3kjlz5m4r13qK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q66N4/dJMcabDY3D4/TfD53xnw3kjlz5m4r13qK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq66N4%2FdJMcabDY3D4%2FTfD53xnw3kjlz5m4r13qK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;404&quot; data-filename=&quot;ㅁ3.png&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그럼 위에서의 정확한 문제는 swapchain과 depthstencil의 count값이 다른것이 였습니다&lt;br /&gt;렌더 파이프라인을 보면 Rasterizer 후에 샘플별로 Pixel Shader를 실행하여 N개의 픽셀값을 구합니다&lt;br /&gt;이후 OutputMerge단계에서 RenderTarget(swapchain)[n]과 depthbuffer[n]을 1:1 비교합니다&lt;br /&gt;기존 픽셀의 depth와 새 depth를 비교하고 새 depth가 더 작다면 rendertarget[n]을 덮어씌우는거죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;하지만 저 갯수가 다르다면 바인딩 자체를 무효화 처리합니다&lt;br /&gt;따라서 갯수의 주체는 RenderTarget입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 D3D11_DSV_DIMENSION_TEXTURE2DMS &amp;lt;- 뒤에 MS(multi-sample)이 붙은걸로 view를 만들어야합니다&lt;/p&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나 FXAA같은 방법을 사용한다고 하네요&lt;br /&gt;-&amp;gt; post-processing 방식&lt;/p&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;특히 로우레벨/api를 다룰 떄는 항상 유의하며 학습해야겠습니다&lt;/p&gt;</description>
      <category>프로젝트 회고록</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/14</guid>
      <comments>https://naeu.tistory.com/14#entry14comment</comments>
      <pubDate>Sat, 25 Apr 2026 16:26:14 +0900</pubDate>
    </item>
    <item>
      <title>Let's Groove - TCP와 UDP</title>
      <link>https://naeu.tistory.com/13</link>
      <description>&lt;div style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left;&quot;&gt;
&lt;div id=&quot;message-content-1478594122964275302&quot; style=&quot;color: oklab(0.964355 0.000418752 -0.00125641);&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://megayuchi.com/2025/10/07/tcp-vs-udp-latency-차이/&quot;&gt;https://megayuchi.com/2025/10/07/tcp-vs-udp-latency-차이/&lt;/a&gt; &lt;/span&gt;&lt;/div&gt;
&lt;div style=&quot;color: oklab(0.964355 0.000418752 -0.00125641);&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;제가 자주 보는 분의 글들을 읽다가 적어봅니다 흔히 속도는 UDP, 안정성은 TCP라고 머리에 박혀있습니다 다만 글의 내용을 보면 오히려 TCP가 빠른 결과가 나왔습니다 전제를 살펴보도록 하죠&lt;/span&gt;&lt;/div&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이미 연결되어 있다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;재전송이 일어나지 않는다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; TCP의 연결은 3-way handshake로 진행됩니다 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Clinet -&amp;gt; Server 연결 요첨 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Server -&amp;gt; Client 연결 확인 + 연결 요청 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Client -&amp;gt; Server 연결 확인&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;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;반면 UDP는 연결이랄게 따로 없습니다 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그냥 존재하는지도 모르는 어딘가에게 계속 데이터를 보내는 거죠 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 TCP는 연결 처리 과정이 존재합니만 전제는 이미 연결되어 있기에 이는 무시하도록 하죠&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;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다음은 재전송입니다 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;재전송은 주로 패킷이 손실되었을 때 진행됩니다 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;TCP의 경우 데이터를 수신 확인인 ACK가 돌아오지 않았기에 다시 전송합니다 반&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;면 UDP는 손실됐는지도 모르기에 다시 처리할 과정이 존재하지 않습니다 따라 재전송이 발생한다면 TCP에서는 처리하는 일이 더 생기기에 늦어진다지만, 이것도 전제에서는 빠졌습니다&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;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&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;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;헤더의 크기가 우선 떠오릅니다 TCP는 데이터의 상태같이 자세한 정보들을 더 기록해야하기에 패킷에서 크기를 더 차지합니다 하지만 그 차이는 현대 하드웨어 기준으로 무시될 수준이기에 실질적 레이턴시의 차이가 미미해지죠 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 결론은 TCP가 모든면에서 더 좋다? 라고 하기엔 사실 전제가 좀 사기인 면이 있죠&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&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;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특히 TCP는 순서가 꽤 중요한 프로토콜이기 때문에 더욱 무시될 수는 없죠 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;데이터가 방대해져 4개로 쪼개서 보낸다고 할때, 1. 통과 2. 손실이 발생하면 다음 3과 4는 블로킹 되어 HOL Blocking이 발생하게 되니까요 &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;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 실제로 TCP만을 사용하기엔 무리가 있지만 글의 내용은 UDP는 빠름! TCP는 느림!이라는 단순한 사고를 경계하자는 취지인 것 같네요 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: oklab(0.298112 0.00109589 -0.00903481); text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;추가로 몇개를 더 찾아봤는데 UDP기반으로 중요한 데이터만 재전송 가능하게 만든 방식도 있다고 합니다 &lt;/span&gt;&lt;/p&gt;</description>
      <category>거창하지 않지만 중요한 개발 잡담</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/13</guid>
      <comments>https://naeu.tistory.com/13#entry13comment</comments>
      <pubDate>Sat, 7 Mar 2026 12:52:27 +0900</pubDate>
    </item>
    <item>
      <title>Spider-Man: Into the Spider-Verse - 멀티스레딩</title>
      <link>https://naeu.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 멀티스레딩에 대해 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 정확히 표현하자면 C++에서 어떻게 다루는지 알아보자&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;따라서 앞으로도 개념정리는 계속 바뀌겠지만 현재까지 이해한 것을 바탕으로 써보겠다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;std::thread&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대입 연산자를 이용하여 함수를 실행하기 전까지는 빈 포인터 상태처럼 남아있다&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void Work()
{
	std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
	std::thread t = std::thread(Work);
	t.join();
}
&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;그리고 이런 스레드에 대해 관리하는 방법은 2가지가 있다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;join();&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 스레드를 지정한 스레드가 끝나기전까지 재운다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 유의할 점은 스레드가 끝난다고해서 std::thread가 죽진 않는다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;detach();&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;분리한다&amp;rdquo;라는 이 함수는 현재 스레드에서 실행 중인 스레드를 분리함으로서 독립적으로 수행 할 수 있게한다&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void Independent()
{
	std::cout &amp;lt;&amp;lt; &quot;Start Independent Work&quot; &amp;lt;&amp;lt; std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(5));
	std::cout &amp;lt;&amp;lt; &quot;End Independent Work&quot; &amp;lt;&amp;lt; std::endl;
}

void Caller()
{
	std::cout &amp;lt;&amp;lt; &quot;Start Call Work&quot; &amp;lt;&amp;lt; std::endl;
	std::thread t = std::thread(Independent);
	std::this_thread::sleep_for(std::chrono::seconds(2));
	std::cout &amp;lt;&amp;lt; &quot;End Call Work&quot; &amp;lt;&amp;lt; std::endl;
}

int main()
{
	std::thread t = std::thread(Caller);
	std::this_thread::sleep_for(std::chrono::seconds(10));
}
&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;바로 &amp;ldquo;End Independent Work&amp;rdquo;가 출력되지 않는다는 것인데, 그 이유는 std::thread는 기본적으로 스코프에 의해 생명이 관리되기 때문이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 5초가 지나 출력되기전에 2초가 지나버려 스레드 자체가 사라진다는 의미!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 우리가 원하는 방향으로 실행하려면&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;void Independent()
{
	std::cout &amp;lt;&amp;lt; &quot;Start Independent Work&quot; &amp;lt;&amp;lt; std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(5));
	std::cout &amp;lt;&amp;lt; &quot;End Independent Work&quot; &amp;lt;&amp;lt; std::endl;
}

void Caller()
{
	std::cout &amp;lt;&amp;lt; &quot;Start Call Work&quot; &amp;lt;&amp;lt; std::endl;
	std::thread t = std::thread(Independent);
	t.detach();
	std::this_thread::sleep_for(std::chrono::seconds(2));
	std::cout &amp;lt;&amp;lt; &quot;End Call Work&quot; &amp;lt;&amp;lt; std::endl;
}

int main()
{
	std::thread t = std::thread(Caller);
	std::this_thread::sleep_for(std::chrono::seconds(10));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 작성하면 된다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;std::mutex&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutex는 일종의 간이영역이라고 생각하면 된다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNpaG8/dJMcahwkVZd/6w8SSIcjw0ekgtdnZJQWMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNpaG8/dJMcahwkVZd/6w8SSIcjw0ekgtdnZJQWMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNpaG8/dJMcahwkVZd/6w8SSIcjw0ekgtdnZJQWMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNpaG8%2FdJMcahwkVZd%2F6w8SSIcjw0ekgtdnZJQWMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;266&quot; height=&quot;190&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutex를 lock 상태로 만들어 범위 내, 혹은 스코프 내의 변수들을 보호한다&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;lsquo;멀티&amp;rsquo;스레드이기 때문에 다른 스레드들이 평행세계마냥 동시에 같이 돌아간다&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;그럼 lock은 어떻게 하는지 알아보도록하자&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;std::lock_guard&lt;/h2&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;std::mutex m;
{
    std::lock_guard&amp;lt;std::mutex&amp;gt; lock(m);
    sharedData++;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lock_guard는 가장 간단한 보호기로 mutex를 스코프 기준으로 보호한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;범위: 생성~스코프 끝날때까지&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;std::unique_lock&lt;/h2&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;std::mutex m;
std::unique_lock&amp;lt;std::mutex&amp;gt; lock(m);
sharedData++;
lock.unlock(); 
// 다시 lock 가능
lock.lock();    
&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;우선 스코프와 상관없이 해제 후 다시 lock이 가능하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 가장 중요한 이유로는 후술 할 std::condition_variable과 연동이 가능하다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;std::condition_variable&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 친구는 잠든 스레드를 깨워서 자신의 mutex 권한이 있는지 확인해보라고 하나의 스레드 혹은 모든 스레드에게 전달할 수 있다&lt;/p&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;cv.wait(lock, [] {
    return !jobQueue.empty();
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태의 코드가 실행될 때 jobQueue에 요소가 있다면 그대로 실행되지만 요소가 없다면 잠들게 된다&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;cv.wait(lock, bool condition);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서는 람다식으로 표현했지만 그냥 bool 조건 검사이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 다시 한번 조건에 부합되는지 확인하기 위해서는 &amp;ldquo;조건이 변경되어서 참일 수도 있으니 한번 확인해봐라&amp;rdquo; 라고 알림을 보낼 수 있다&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;cv.notify_one(); //무작위 하나만 검사 요청
cv.notify_all(); //cv로 wait하는 모든 스레드에 검사 요청
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 검사를 진행했더니 조건이 참이라면, 해당 wait 아래에 있는 코드들을 mutex 권한을 가진채로 실행한다&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;style8&quot; /&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;</description>
      <category>C++과 친해지기</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/12</guid>
      <comments>https://naeu.tistory.com/12#entry12comment</comments>
      <pubDate>Wed, 7 Jan 2026 22:08:45 +0900</pubDate>
    </item>
    <item>
      <title>Little Mo' Betta - 구조체 데이터 최적화하기</title>
      <link>https://naeu.tistory.com/11</link>
      <description>&lt;pre id=&quot;code_1765596456928&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Bonfire {
    bool bIsSleeping; 
    int nMemberCount; 
    bool bIsHappy; 
    bool bCanJoinNow; 
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 구조체의 크기는 몇일까?&lt;br /&gt;우리가 알고 있는 지식으로는 bool이 1바이트, int는 4바이트이다&lt;br /&gt;따라서 bool x3 + int x1로 7바이트라고 생각할 수 있다&lt;br /&gt;그렇다면 돌려보자&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;int main() {
	Bonfire fire;
	std::cout &amp;lt;&amp;lt; &quot;Size of Bonfire: &quot; &amp;lt;&amp;lt; sizeof(fire) &amp;lt;&amp;lt; std::endl;
	return 0;
}&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;t3.png&quot; data-origin-width=&quot;169&quot; data-origin-height=&quot;59&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clDOKY/dJMcac9rnFQ/Nxtdi0GUr6JPvdEBzo77j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clDOKY/dJMcac9rnFQ/Nxtdi0GUr6JPvdEBzo77j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clDOKY/dJMcac9rnFQ/Nxtdi0GUr6JPvdEBzo77j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclDOKY%2FdJMcac9rnFQ%2FNxtdi0GUr6JPvdEBzo77j0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;169&quot; height=&quot;59&quot; data-filename=&quot;t3.png&quot; data-origin-width=&quot;169&quot; data-origin-height=&quot;59&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;거의 2배인 12바이트가 할당되어버렸다&lt;br /&gt;왜 그럴까?&lt;br /&gt;첫번째로는 애초에 7바이트라는 사이즈만 할당할 수 없다&lt;br /&gt;CPU에서는 값을 읽을때 4바이트로 정렬되어있다면 값을 한번에 읽을 수 있다&lt;br /&gt;그렇기 때문에 컴파일러는 ABI(Application Binary Interface)라고 하는 규약을 따라 4바이트씩 데이터를 정렬한다&lt;br /&gt;따라서 모든 데이터들은 1-4-8-16... 기준으로 할당된다&lt;br /&gt;자 그럼 7이 안 나오는건 이해가 됐다&lt;br /&gt;그럼 8이 나와야지 왜 12가 나오는걸까?&lt;br /&gt;그 이유는 값을 정렬할때 패딩(padding)이라는 것이 들어가기 때문이다&lt;br /&gt;만약 저 구조체의 메모리 레이아웃을 그려본다고 하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;cpp 
b: bool, i: int
&lt;br /&gt;[b][ ][ ][ ] / [ ][ ][ ][ ] / [ ][ ][ ][ ]&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 bIsSleeping에 해당하는 1바이트 bool을 삽입하였다&lt;br /&gt;이 제 뭐 하 지 ?&lt;br /&gt;다음으로는 4바이트 int를 삽입해야하는데 b 바로 다음에 넣게된다면 정렬 규칙이 깨져버리게 된다&lt;br /&gt;따라서 컴파일러는 3바이트의 패딩값을 b다음에 먼저 삽입한다&lt;br /&gt;그리고 그 후에 4바이트 int를 넣는다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;b: bool, i: int, p: padding
&lt;br /&gt;[b][p][p][p] / [i][i][i][i] / [ ][ ][ ][ ]&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벌써 8바이트나 먹었다&lt;br /&gt;이제 bool이 2개 남았다&lt;br /&gt;먼저 삽입해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;b: bool, i: int, p: padding
&lt;br /&gt;[b][p][p][p] / [i][i][i][i] / [b][ ][ ][ ]&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 1바이트 bool을 하나 더 삽입해야하지만&lt;br /&gt;남은 크기를 보니 3바이트라는 무려 3배나 더 되는 크기가 남아있다&lt;br /&gt;따라서 컴파일러는 b바로 다음에 bool을 하나 삽입한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;b: bool, i: int, p: padding
&lt;br /&gt;[b][p][p][p] / [i][i][i][i] / [b][b][ ][ ]&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 구조체 자체의 변수가 더 없기 때문에 이 또한 4바이트씩 맞춰줘야한다&lt;br /&gt;고로, 마지막에 패딩을 넣는다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;b: bool, i: int, p: padding
&lt;br /&gt;[b][p][p][p] / [i][i][i][i] / [b][b][p][p]&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 완성된구조체의 메모리 레이아웃은 총 12바이트를 할당받고 있다&lt;br /&gt;vs에서도 확인해보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;t1.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CC9eI/dJMcafZqZnW/yXgC95Qf7dm8l1BQlqueR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CC9eI/dJMcafZqZnW/yXgC95Qf7dm8l1BQlqueR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CC9eI/dJMcafZqZnW/yXgC95Qf7dm8l1BQlqueR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCC9eI%2FdJMcafZqZnW%2FyXgC95Qf7dm8l1BQlqueR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;140&quot; data-filename=&quot;t1.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;140&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;아니 내가 쓰는건 7바이트 뿐인데&lt;br /&gt;총 공간은 12바이트나 먹고 있다&lt;br /&gt;무려 5바이트나 놀고 있는 것이다&lt;br /&gt;고작 5바이트라고 생각할 수 있지만&lt;br /&gt;이러한 객체가 굉장히 많아진다면&lt;br /&gt;전체 용량에 약 41%를 낭비하고 있는거다&lt;br /&gt;따라서 우리는 아주 살짝의 터치로 이를 보완할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1765596523496&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Bonfire { 
	int nMemberCount; 
    bool bIsSleeping;
    bool bIsHappy; 
    bool bCanJoinNow; 
    };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;끝이다&lt;br /&gt;bool을 한곳으로 모아두면 메모리 레이아웃은&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;t2.png&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/34YSk/dJMcahXgD9K/I4EnUkWEiHLpeq7vx45xOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/34YSk/dJMcahXgD9K/I4EnUkWEiHLpeq7vx45xOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/34YSk/dJMcahXgD9K/I4EnUkWEiHLpeq7vx45xOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F34YSk%2FdJMcahXgD9K%2FI4EnUkWEiHLpeq7vx45xOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;115&quot; data-filename=&quot;t2.png&quot; data-origin-width=&quot;344&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;이렇게 바뀌게 된다&lt;br /&gt;총 크기 8바이트에 놀고 있는 용량도 1바이트밖에 안된다&lt;br /&gt;이렇게 단순히 변수의 순서만으로도 최적화에 도움이 될 수 있다&lt;/p&gt;</description>
      <category>거창하지 않지만 중요한 개발 잡담</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/11</guid>
      <comments>https://naeu.tistory.com/11#entry11comment</comments>
      <pubDate>Sat, 13 Dec 2025 12:32:19 +0900</pubDate>
    </item>
    <item>
      <title>Fight Club - Rule of 0/3/5</title>
      <link>https://naeu.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;C++을 공부하다 보면&lt;br /&gt;Rule of 5, Rule of 3, Rule of 0라는 뭔 규칙을 의미하는건가 싶은 것을 볼 수 있다&lt;br /&gt;오늘은 이 Rule of 0/3/5 에 대해 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Rule of 3와 5은 동일하다&lt;br /&gt;정확히하자면 기존의 Rule of 3에서 C++11 이후부턴 2가지의 규칙이 추가되어 5가 된것이다&lt;br /&gt;이 이야기는 살짝 미뤄두고 Rule of 0부터 확인해보겠다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule of 0&lt;br /&gt;핵심만 읊자면 클래스가 직접 메모리,파일,핸들 등을 관리하지 않게 설계하라는 의미이다&lt;br /&gt;아니 그럼 데이터들을 어떻게 관리하냐? -&amp;gt; RAII를 사용하면 된다&lt;br /&gt;RAII는 Resource acquisition is initialization의 약자로, &quot;자원 획득은 초기화&quot;라는 의미이다&lt;br /&gt;RAII는 자원은 해당 객체가 살아있을 때만 보유되는 것을 보장하는 규칙이다&lt;br /&gt;따라서 객체 누수가 없다면 그 안에 있는 자원 누수도 없어지니 자원 관리가 간편해진다&lt;br /&gt;쉬운 예시로는 std::vector가 있다&lt;br /&gt;std::vector는 내부적으로 Heap에 메모리를 할당하는 동적 할당을 사용한다&lt;br /&gt;그렇기에 사용이 완료된 후에는 할당한 메모리를 해제해주어야하는데, std::vector는 이를 소멸자에서 처리하게 두어 사용자가 std::vector를 사용할때 굳이 신경쓰지 않아도 되게끔 만들었다&lt;br /&gt;이게 Rule of 0의 좋은 예시이다&lt;br /&gt;가장 안전하고, 버그가 좋고, 유지보수 비용이 낮다&lt;br /&gt;Rule of 3&lt;br /&gt;그렇다면 클래스가 리소스를 직접 관리할때는 어떤 것들이 필요할까?&lt;br /&gt;해당 클래스 혹은 타입에 대해서 무조건&lt;br /&gt;복사 생성자&lt;br /&gt;복사 대입 연산자&lt;br /&gt;소멸자&lt;br /&gt;이 3개를 정의해야한다라고 말하는 것이 이 Rule of 3이다&lt;br /&gt;class Data {&lt;br /&gt;public:&lt;br /&gt;Data(size_t size): m_data(new int[size]), m_size(size) {}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private:&lt;br /&gt;int* m_data;&lt;br /&gt;size_t m_size;&lt;br /&gt;};&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 데이터가 이렇게 존재할때 우선 이 데이터가 사라질때 만들어둔 m_data를 메모리에서 해제시켜주어야한다&lt;br /&gt;class Data {&lt;br /&gt;public:&lt;br /&gt;Data(size_t size): m_data(new int[size]), m_size(size) {}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//소멸자&lt;br /&gt;~Data() {&lt;br /&gt;delete[] m_data;&lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private:&lt;br /&gt;int* m_data;&lt;br /&gt;size_t m_size;&lt;br /&gt;};&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 나선&lt;br /&gt;Data newData(existData);&lt;br /&gt;Data newData = existData;&lt;br /&gt;방식으로 값을 복사해서 사용하고 싶다면&lt;br /&gt;class Data {&lt;br /&gt;public:&lt;br /&gt;Data(size_t size): m_data(new int[size]), m_size(size) {}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//복사 생성자&lt;br /&gt;Data(const Data&amp;amp; other): m_data(new int[other.m_size]), m_size(other.m_size) {&lt;br /&gt;std::copy(other.m_data, other.m_data + m_size, m_data);&lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//복사 대입 연산자&lt;br /&gt;Data&amp;amp; operator=(const Data&amp;amp; other) {&lt;br /&gt;if (this != &amp;amp;other) {&lt;br /&gt;delete[] m_data;&lt;br /&gt;m_size = other.m_size;&lt;br /&gt;m_data = new int[m_size];&lt;br /&gt;std::copy(other.m_data, other.m_data + m_size, m_data);&lt;br /&gt;}&lt;br /&gt;return *this;&lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//소멸자&lt;br /&gt;~Data() {&lt;br /&gt;delete[] m_data;&lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;public:&lt;br /&gt;int* m_data;&lt;br /&gt;size_t m_size;&lt;br /&gt;};&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 둘의 차이라고 한다면 복사 생성은 데이터를 처음 생성할때 기존의 있는 데이터 값을 복사해서 생성한다는 것이고,&lt;br /&gt;복사 대입 연산자는 이미 있는 데이터의 값을 다른 데이터로 바꾸고 싶을때 복사해서 사용한다는 차이가 있다&lt;br /&gt;이렇게 3개의 규칙을 알아봤는데 C++11 이후부턴 드디어 그 녀석이 생겨났다&lt;br /&gt;Rule of 5&lt;br /&gt;std::move();&lt;br /&gt;포인터가 C++ 뉴비들의 첫 벽이라면 얘는 뉴비 분쇄기다&lt;br /&gt;이동 시멘틱에 대해선 추후에 자세히 다루기로 하고 우선 나머지 2개의 규칙을 알아보자&lt;br /&gt;이동 생성자&lt;br /&gt;이동 대입 연산자&lt;br /&gt;move라는 이동이 추가되면서 더 이상 비효율적인 복사 대신에 전체를 욺겨버릴 수 있는 효율적인 방법이 생겨났다&lt;br /&gt;class Data {&lt;br /&gt;public:&lt;br /&gt;Data(size_t size): m_data(new int[size]), m_size(size) {}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//이동 생성자&lt;br /&gt;Data(Data&amp;amp;&amp;amp; other): m_data(new int[other.m_size]), m_size(other.m_size) {&lt;br /&gt;other.m_data = nullptr;&lt;br /&gt;other.m_size = 0;&lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//이동 대입 연산자&lt;br /&gt;Data&amp;amp; operator=(Data&amp;amp;&amp;amp; other) {&lt;br /&gt;if (this != &amp;amp;other) {&lt;br /&gt;delete[] m_data;&lt;br /&gt;m_size = other.m_size;&lt;br /&gt;m_data = new int[m_size];&lt;br /&gt;std::copy(other.m_data, other.m_data + m_size, m_data);&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;  other.m_data = nullptr;
  other.m_size = 0;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;return *this;&lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//소멸자&lt;br /&gt;~Data() {&lt;br /&gt;delete[] m_data;&lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;public:&lt;br /&gt;int* m_data;&lt;br /&gt;size_t m_size;&lt;br /&gt;};&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복사와 큰 차이점을 짚는다면 변수를 받아올 때 &amp;amp;&amp;amp;(Rvalue 참조 선언자)로 받아온다는 것과&lt;br /&gt;other.m_data = nullptr;&lt;br /&gt;other.m_size = 0;&lt;br /&gt;로 기존에 있던 값들을 리셋한다는 것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 데이터를 정의할때 이 5가지를 꼭 기억해보자&lt;/p&gt;</description>
      <category>C++과 친해지기</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/10</guid>
      <comments>https://naeu.tistory.com/10#entry10comment</comments>
      <pubDate>Sat, 13 Dec 2025 11:25:29 +0900</pubDate>
    </item>
    <item>
      <title>Nameless - 좋은 이름을 짓는 방법</title>
      <link>https://naeu.tistory.com/9</link>
      <description>&lt;p&gt;언어별 예약어를 사용하지 말것 ex) int, float, for etc&lt;br&gt;컴파일러 예약어도 사용하지 말것 ex) __언더바2개, _Muck언더바후대문자&lt;br&gt;모두가 알고있는 약어만 사용할 것&lt;br&gt;같은 내용을 반복하지 말것&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cs&quot;&gt;void saveEmployee(const Employee employee);
보다는
void save(const Employee employee);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;프로퍼티도 이름의 일부로서 사용&lt;br&gt;bool bIsNotPlayer 처럼 논리형에 부정문 사용하지 않기 =&amp;gt; bIsPlayer&lt;br&gt;귀찮더라도 임시변수에 이름 지정 ex) for문의 i, iterator의 it, templete의 T&lt;br&gt;어떻게 구현했지는지 생각하지 않기 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cs&quot;&gt;pair&amp;lt;Player, int&amp;gt; GetPlayerLevelPairInt(const std::string name);
보다는
pair&amp;lt;Player, int&amp;gt; GetPlayerLevel(const std::string name);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 상황에서 &amp;quot;같은 내용 반복하지 말라&amp;quot;는 의미로 GetLevel로 함수명을 지정하면 안되냐 생각될 수도 있지만 리턴값이 아닌 프로퍼티에 한정하기 때문에 다름&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cs&quot;&gt;pair&amp;lt;Player, int&amp;gt; GetLevel(const Player player);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;라면 괜찮음&lt;/p&gt;</description>
      <category>거창하지 않지만 중요한 개발 잡담</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/9</guid>
      <comments>https://naeu.tistory.com/9#entry9comment</comments>
      <pubDate>Sat, 6 Dec 2025 10:23:12 +0900</pubDate>
    </item>
    <item>
      <title>Still Alive - RAII에 대하여</title>
      <link>https://naeu.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;Resource Acquisition Is Initialization&quot;&lt;/b&gt;&lt;br /&gt;번역하자면,&lt;br /&gt;&lt;b&gt;&quot;객체와 자원의 라이프 사이클을 일치시킨다&quot;&lt;/b&gt;&lt;br /&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;br /&gt;왜 그래야하지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이유는 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 커지고 여러 데이터들이 생명주기를 직접 관리해야한다면 인간인 이상 놓치는 부분이 생기기 때문이다&lt;br /&gt;또한 놓치지 않더라도 이 데이터를 누가 참조하는 지에 대한 것도 불명확하기에 이를 방지하고자 지향해야한다&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;class Player
{
   public:
      Player()
      {
         m_inventory = new Inventory;
      }
      ~Player();
      {
         delete m_inventory;
         m_inventory = nullptr;
      }
   private:
      Inventory* m_inventory;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 위의 코드는 RAII를 준수하였을까?&lt;br /&gt;내부 변수로 Inventory를 동적할당하여 사용하고, Player가 삭제될 때도 함께 delete하였으니 준수했다고 볼 수 있다&lt;/p&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++에서는 이를 스마트 포인터를 이용하여 관리하는 것을 권장한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&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;li&gt;대입 연산자/복사 생성자 등에서의 안정성: Rule of Five 대응&lt;/li&gt;
&lt;li&gt;자원 관리의 명시성과 추론 용이성: 코드의 의도가 명확해짐&lt;br /&gt;정도이다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포인트는 개발자가 관리에 대한 책임을 갖지 않으면 그냥 잊어먹어도 괜찮다는 원칙이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 모던 C++에서는 스마트 포인터를 활용하여 RAII를 자동화하고 안전성을 높이는 방식이 바람직하다&lt;/p&gt;</description>
      <category>거창하지 않지만 중요한 개발 잡담</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/8</guid>
      <comments>https://naeu.tistory.com/8#entry8comment</comments>
      <pubDate>Mon, 21 Jul 2025 16:44:56 +0900</pubDate>
    </item>
    <item>
      <title>OpenGL 공부하기 #4 - 행렬과 변환</title>
      <link>https://naeu.tistory.com/7</link>
      <description>&lt;h3 data-end=&quot;304&quot; data-start=&quot;280&quot; data-ke-size=&quot;size23&quot;&gt;1. glm 라이브러리 기본&lt;/h3&gt;
&lt;p data-end=&quot;365&quot; data-start=&quot;306&quot; data-ke-size=&quot;size16&quot;&gt;GLM (OpenGL Mathematics)은 GLSL과 유사한 문법을 사용하는 수학 라이브러리&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752500996254&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;glm/glm.hpp&amp;gt;
#include &amp;lt;glm/gtc/matrix_transform.hpp&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;480&quot; data-start=&quot;451&quot; data-ke-size=&quot;size23&quot;&gt;2. Transformations의 종류&lt;/h3&gt;
&lt;h4 data-end=&quot;505&quot; data-start=&quot;482&quot; data-ke-size=&quot;size20&quot;&gt;Translation (이동)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752501074760&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(x, y, z));&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;649&quot; data-start=&quot;603&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;627&quot; data-start=&quot;603&quot;&gt;객체를 x, y, z 방향으로 이동시킴&lt;/li&gt;
&lt;li data-end=&quot;649&quot; data-start=&quot;628&quot;&gt;보통 model 행렬에서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;673&quot; data-start=&quot;651&quot; data-ke-size=&quot;size20&quot;&gt;Scaling (크기 조절)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752501064863&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model = glm::scale(model, glm::vec3(sx, sy, sz));&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;균일 스케일: glm::vec3(s, s, s)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h4 data-end=&quot;804&quot; data-start=&quot;784&quot; data-ke-size=&quot;size20&quot;&gt;Rotation (회전)&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752501183930&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;model = glm::rotate(model, glm::radians(angle), glm::vec3(ax, ay, az));&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;927&quot; data-start=&quot;888&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;913&quot; data-start=&quot;888&quot;&gt;angle: 회전 각도 (라디안 단위)&lt;/li&gt;
&lt;li data-end=&quot;927&quot; data-start=&quot;914&quot;&gt;vec3: 회전축&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;938&quot; data-start=&quot;929&quot; data-ke-size=&quot;size16&quot;&gt;예) Z축 회전:&lt;/p&gt;
&lt;pre id=&quot;code_1752501195970&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glm::rotate(model, glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;h3 data-end=&quot;301&quot; data-start=&quot;270&quot; data-ke-size=&quot;size23&quot;&gt;3. Model Matrix (모델 행렬)&lt;/h3&gt;
&lt;p data-end=&quot;333&quot; data-start=&quot;302&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Local Space &amp;rarr; World Space&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;378&quot; data-start=&quot;334&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;352&quot; data-start=&quot;334&quot;&gt;로컬 좌표를 월드 좌표로 변환&lt;/li&gt;
&lt;li data-end=&quot;378&quot; data-start=&quot;353&quot;&gt;객체 개별 위치, 회전, 스케일 등을 반영&lt;/li&gt;
&lt;li data-end=&quot;378&quot; data-start=&quot;353&quot;&gt;순서: 크기 &lt;b&gt;&amp;rarr;&lt;/b&gt; 회전&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr;&lt;/b&gt; 위치&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752501702148&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glm::mat4 model = glm::mat4(1.0f); 
model = glm::scale(model, glm::vec3(scale, scale, scale));
model = glm::rotate(model, glm::radians(angle), glm::vec3(0, 0, 1)); 
model = glm::translate(model, glm::vec3(x, y, z));&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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_1752502304549&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glm::mat4 mvp = projection * view * model;
glUniformMatrix4fv(mvpLocation, 1, GL_FALSE, glm::value_ptr(mvp));&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;640&quot; data-start=&quot;611&quot; data-ke-size=&quot;size23&quot;&gt;4. View Matrix (뷰 행렬)&lt;/h3&gt;
&lt;p data-end=&quot;680&quot; data-start=&quot;641&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;World Space &amp;rarr; View (Camera) Space&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;732&quot; data-start=&quot;681&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;704&quot; data-start=&quot;681&quot;&gt;카메라 위치/시점 기준으로 좌표를 변환&lt;/li&gt;
&lt;li data-end=&quot;732&quot; data-start=&quot;705&quot;&gt;glm::lookAt() 함수를 자주 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752501994569&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glm::mat4 view = glm::lookAt( 
	glm::vec3(0.0f, 0.0f, 3.0f), // Camera position 
	glm::vec3(0.0f, 0.0f, 0.0f), // Target 
	glm::vec3(0.0f, 1.0f, 0.0f) // Up 
);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;954&quot; data-start=&quot;918&quot; data-ke-size=&quot;size23&quot;&gt;5. Projection Matrix (투영 행렬)&lt;/h3&gt;
&lt;p data-end=&quot;982&quot; data-start=&quot;955&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;View Space &amp;rarr; Clip Space&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1005&quot; data-start=&quot;984&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1005&quot; data-start=&quot;984&quot;&gt;원근 투영: 원근감 있는 장면 표현&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752502008860&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glm::mat4 projection = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1120&quot; data-start=&quot;1102&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1120&quot; data-start=&quot;1102&quot;&gt;직교 투영: 일정한 비율 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752502016171&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glm::mat4 projection = glm::ortho(left, right, bottom, top, near, far);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1239&quot; data-start=&quot;1210&quot; data-ke-size=&quot;size23&quot;&gt;6. Clip Space (클립 공간)&lt;/h3&gt;
&lt;p data-end=&quot;1268&quot; data-start=&quot;1240&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-w ~ +w 사이로 정점 위치가 재조정됨.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1435&quot; data-start=&quot;1270&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1334&quot; data-start=&quot;1270&quot;&gt;MVP = projection * view * model 을 적용한 후의 결과는 Clip Space에 있음.&lt;/li&gt;
&lt;li data-end=&quot;1392&quot; data-start=&quot;1335&quot;&gt;GPU는 이 공간에서 &lt;b&gt;Clipping (잘라냄)&lt;/b&gt; 을 수행함. 즉, 화면 밖의 좌표를 제거함.&lt;/li&gt;
&lt;li data-end=&quot;1435&quot; data-start=&quot;1393&quot;&gt;이 시점에서는 정점 좌표는 &lt;b&gt;vec4(x, y, z, w)&lt;/b&gt; 형태임.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752502030856&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;gl_Position = projection * view * model * vec4(vertexPos, 1.0);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1565&quot; data-start=&quot;1519&quot; data-ke-size=&quot;size23&quot;&gt;7. NDC (Normalized Device Coordinates)&lt;/h3&gt;
&lt;p data-end=&quot;1595&quot; data-start=&quot;1566&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Clip Space &amp;rarr; NDC (나눗셈 처리)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1750&quot; data-start=&quot;1597&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1647&quot; data-start=&quot;1597&quot;&gt;GPU가 자동으로 gl_Position.xyz / gl_Position.w 를 해줌&lt;/li&gt;
&lt;li data-end=&quot;1750&quot; data-start=&quot;1648&quot;&gt;결과는 각 축의 값이 &lt;b&gt;-1.0 ~ 1.0&lt;/b&gt; 사이로 정규화됨&lt;br /&gt;(X: 왼쪽 -1, 오른쪽 1 / Y: 아래 -1, 위 1 / Z: near -1, far 1 or 0~1)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1755&quot; data-start=&quot;1752&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1802&quot; data-start=&quot;1757&quot; data-ke-size=&quot;size23&quot;&gt;8. Viewport Transform &amp;rarr; Screen Space&lt;/h3&gt;
&lt;p data-end=&quot;1825&quot; data-start=&quot;1803&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NDC &amp;rarr; Screen Space&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1911&quot; data-start=&quot;1827&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1874&quot; data-start=&quot;1827&quot;&gt;glViewport(x, y, width, height) 설정값 기준으로 변환&lt;/li&gt;
&lt;li data-end=&quot;1911&quot; data-start=&quot;1875&quot;&gt;-1 ~ 1 범위의 NDC가 &amp;rarr; 실제 화면 픽셀 좌표로 매핑됨&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752502053283&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glViewport(0, 0, screenWidth, screenHeight);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2058&quot; data-start=&quot;1974&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1998&quot; data-start=&quot;1974&quot;&gt;NDC (0, 0) &amp;rarr; 화면의 정중앙&lt;/li&gt;
&lt;li data-end=&quot;2028&quot; data-start=&quot;1999&quot;&gt;NDC (1, 1) &amp;rarr; 화면 오른쪽 위 모서리&lt;/li&gt;
&lt;li data-end=&quot;2058&quot; data-start=&quot;2029&quot;&gt;NDC (-1, -1) &amp;rarr; 화면 왼쪽 아래 모서리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2079&quot; data-start=&quot;2065&quot; data-ke-size=&quot;size23&quot;&gt;전체 수식 정리&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752502105062&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;glm::mat4 mvp = projection * view * model; 
gl_Position = mvp * vec4(vertex, 1.0f);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2185&quot; data-start=&quot;2176&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2185&quot; data-start=&quot;2176&quot; data-ke-size=&quot;size16&quot;&gt;파이프라인 흐름:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1752502115347&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Local Space] &amp;rarr; model &amp;rarr; [World Space] &amp;rarr; view &amp;rarr; [View (Camera) Space] &amp;rarr; projection &amp;rarr; [Clip Space] &amp;rarr; perspective divide &amp;rarr; [NDC] &amp;rarr; viewport transform &amp;rarr; [Screen Space]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k9A27/btsPixnq5Zl/4h7U54EqaK24M39zMzRsj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k9A27/btsPixnq5Zl/4h7U54EqaK24M39zMzRsj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k9A27/btsPixnq5Zl/4h7U54EqaK24M39zMzRsj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk9A27%2FbtsPixnq5Zl%2F4h7U54EqaK24M39zMzRsj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;394&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>OpenGL</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/7</guid>
      <comments>https://naeu.tistory.com/7#entry7comment</comments>
      <pubDate>Mon, 14 Jul 2025 23:13:44 +0900</pubDate>
    </item>
    <item>
      <title>OpenGL 개발하기 - Sierpinski Triangle</title>
      <link>https://naeu.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;재귀함수를 이용해 시에르핀스키 삼각형을 그리는 코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 효율적으로 그리기 위해서는 재귀함수에 glDrawElements를 하지 않고 vector타입을 이용해 vertices에 정점을 더해가며 재귀가 마친 후에 VBO에 한번에 넣는 방식이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그려보는 것이 목적이었기에 단순한 방식을 채택했다&lt;/p&gt;
&lt;pre id=&quot;code_1752498849599&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;glad/glad.h&amp;gt;
#include &amp;lt;GLFW/glfw3.h&amp;gt;

#include &amp;lt;glm/glm.hpp&amp;gt;
#include &amp;lt;glm/gtc/matrix_transform.hpp&amp;gt;
#include &amp;lt;glm/gtc/type_ptr.hpp&amp;gt;

#include &quot;Shader.h&quot;
#define STB_IMAGE_IMPLEMENTATION
#include &quot;stb_image.h&quot;


float vertices[] = {
// positions          // colors           // texture coords
1.0f,  1.0f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f,  1.0f,   // top right
0.0f, -1.0f, 0.0f,   0.0f, 1.0f, 0.0f,   0.5f,  0.0f,   // bottom center
   -1.0f,  1.0f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f,  1.0f,   // top left
};

unsigned int indices[] = { 
0, 1, 2
};

const int level = 5;
void recursive_triangle(unsigned int transformLoc, const glm::vec2&amp;amp; top, const glm::vec2&amp;amp; right, const glm::vec2&amp;amp; left, int depth) {
	if (depth &amp;gt; level) return;

	//draw
	glm::mat4 trans = glm::mat4(1.0f); // 단위 행렬 1로 초기화 (대각선으로만 1인것)
	trans = glm::translate(trans, glm::vec3(top.x, (top.y - right.y) / 4 + right.y, 0.0f)); // 위치
	float scale = pow(0.5f, depth);
	trans = glm::scale(trans, glm::vec3(scale, scale, 1.0f)); // 크기
	glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

	glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
	depth++;

	glm::vec2 topRight = (top + right) * 0.5f;
	glm::vec2 bottom = (right + left) * 0.5f;
	glm::vec2 topLeft = (left + top) * 0.5f;

	recursive_triangle(transformLoc, top, topRight, topLeft, depth); //top
	recursive_triangle(transformLoc, topRight, right, bottom, depth); //right
	recursive_triangle(transformLoc, topLeft, bottom, left, depth); //left
}

void processInput(GLFWwindow* window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
	{
	glfwSetWindowShouldClose(window, true);
	}
}

int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	GLFWwindow* m_window = glfwCreateWindow(800, 800, &quot;title&quot;, NULL, NULL);
	if (m_window == NULL)
	{
		std::cout &amp;lt;&amp;lt; &quot;Fail to create window&quot; &amp;lt;&amp;lt; std::endl;
		glfwTerminate();
		return -1;
	}

	glfwMakeContextCurrent(m_window);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout &amp;lt;&amp;lt; &quot;Fail to initialize GLAD&quot; &amp;lt;&amp;lt; std::endl;
		return -1;
		}

	glViewport(0, 0, 800, 400 * sqrt(3));

	// 쉐이더
	Shader testShader(&quot;TestVertexShader.vert&quot;, &quot;TestFragmentShader.frag&quot;);

	// VAO
	unsigned int VAO;
	glGenVertexArrays(1, &amp;amp;VAO);

	// VBO
	unsigned int VBO; // Vertex Buffer Object
	glGenBuffers(1, &amp;amp;VBO); // 버퍼 id 생성

	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO); // VBO를 GL_ARRAY_BUFFER에 바인딩. GL_ARRAY_BUFFER 관련 작업은 VBO에 적용
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //VBO에 vertices 값을 복사해 넣음

	// VAO
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0); // location 0값
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1); // location 1값
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
	glEnableVertexAttribArray(2); // location 2값


	// EBO
	unsigned int EBO;
	glGenBuffers(1, &amp;amp;EBO);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);


	unsigned int texture1, texture2;
	glGenTextures(1, &amp;amp;texture1);
	glBindTexture(GL_TEXTURE_2D, texture1);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); // set texture wrapping to GL_REPEAT (default wrapping method)
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	int width, height, nrChannels;
	stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
	
    unsigned char* data = stbi_load(&quot;Textures/pepe.png&quot;, &amp;amp;width, &amp;amp;height, &amp;amp;nrChannels, 0);
	if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
		std::cout &amp;lt;&amp;lt; &quot;Failed to load texture&quot; &amp;lt;&amp;lt; std::endl;
	}
	stbi_image_free(data);

	testShader.use(); // don't forget to activate/use the shader before setting uniforms!
	// 한 프래그먼트에는 0~15까지 16개의 택스처를 동시에 사용 가능함
	testShader.setInt(&quot;texture1&quot;, 0);

	while (!glfwWindowShouldClose(m_window))
	{
		// input
		processInput(m_window);

		// render
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texture1);
		testShader.use();

		unsigned int transformId = glGetUniformLocation(testShader.ID, &quot;transform&quot;);
		recursive_triangle(transformId, glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, -1.0f), glm::vec2(-1.0f, -1.0f), 1);


		// check and call event and swap
		glfwPollEvents();
		glfwSwapBuffers(m_window);
	}
	glDeleteVertexArrays(1, &amp;amp;VAO);
	glDeleteBuffers(1, &amp;amp;VBO);
	glDeleteBuffers(1, &amp;amp;EBO);
	glfwTerminate();

	return 0;
}&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;image.png&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxj5fd/btsPgZrSQ78/UDrkniQWw5T70Qvkhly6kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxj5fd/btsPgZrSQ78/UDrkniQWw5T70Qvkhly6kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxj5fd/btsPgZrSQ78/UDrkniQWw5T70Qvkhly6kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxj5fd%2FbtsPgZrSQ78%2FUDrkniQWw5T70Qvkhly6kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;803&quot; height=&quot;852&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>OpenGL</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/6</guid>
      <comments>https://naeu.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 14 Jul 2025 22:23:05 +0900</pubDate>
    </item>
    <item>
      <title>OpenGL 공부하기 #3 - VAO, VBO, EBO</title>
      <link>https://naeu.tistory.com/5</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;VAO (Vertex Array Object)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정점의 속성 구조를 저장하는 객체임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버텍스를 어떻게 해석할지 정의함 ex. 첫 3개 float은 xyz, 4번째부터 3개 flaot은 색상값 등&lt;br /&gt;glGenVertexArrays()로 생성함&lt;br /&gt;glBindVertexArray()로 바인딩 후 설정 저장 및 사용 가능&lt;br /&gt;여러 VAO 생성하여 각각 다른 설정 가능&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;VBO (Vertex Buffer Object)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 정점 데이터를 GPU에 저장함&lt;br /&gt;glGenBuffers()로 생성함&lt;br /&gt;glBindBuffer(GL_ARRAY_BUFFER, id)로 바인딩함&lt;br /&gt;glBufferData()로 버텍스 데이터 전송함 (데이터 복사 후 삽입)&lt;br /&gt;GL_STATIC_DRAW &amp;rarr; 자주 바뀌지 않는 데이터에 사용함&lt;/p&gt;
&lt;pre class=&quot;glsl&quot;&gt;&lt;code&gt;float vertices[] = {
    x, y, z, r, g, b
};&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EBO (Element Buffer Object)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정점 인덱스를 통해 중복 없이 재사용 가능함&lt;br /&gt;glGenBuffers() &amp;rarr; EBO 생성함&lt;br /&gt;glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)로 바인딩함&lt;br /&gt;glDrawElements() 호출 시 사용함&lt;br /&gt;예제에서는 glDrawArrays() 사용 중 &amp;rarr; EBO 사용 안함&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;unsigned int indices[] = { 
    0, 1, 2,  
    3, 4, 5
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;glVertexAttribPointer() - VAO&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버텍스 구조를 OpenGL에 설명함&lt;br /&gt;ex) location 0 &amp;rarr; 위치, location 1 &amp;rarr; 색상&lt;br /&gt;stride, offset으로 분리된 구조 처리 가능함&lt;br /&gt;설정 후 glEnableVertexAttribArray()로 활성화함&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void)0); // 위치
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void)(3*sizeof(float))); // 색상&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Shader (가볍게)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vertex shader &amp;rarr; 정점 위치 처리함&lt;br /&gt;fragment shader &amp;rarr; 픽셀 색상 결정함&lt;br /&gt;glCreateShader(), glShaderSource(), glCompileShader()로 생성 가능&lt;br /&gt;glAttachShader(), glLinkProgram()으로 프로그램 연결함&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;while (!glfwWindowShouldClose(window)) {
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(shaderProgram);
    glBindVertexArray(VAO[0]);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glUseProgram(shaderProgram2);
    glBindVertexArray(VAO[1]);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glfwSwapBuffers(window);
    glfwPollEvents();

}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리 및 해제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;glDeleteBuffers(), glDeleteVertexArrays()로 메모리 해제 가능&lt;br /&gt;glDeleteProgram()으로 셰이더 프로그램 삭제 가능&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;791&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ovKFy/btsO6gFXUzW/Bm92KktvK6VxmpK5XTdNb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ovKFy/btsO6gFXUzW/Bm92KktvK6VxmpK5XTdNb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ovKFy/btsO6gFXUzW/Bm92KktvK6VxmpK5XTdNb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FovKFy%2FbtsO6gFXUzW%2FBm92KktvK6VxmpK5XTdNb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;842&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>OpenGL</category>
      <author>NAEU</author>
      <guid isPermaLink="true">https://naeu.tistory.com/5</guid>
      <comments>https://naeu.tistory.com/5#entry5comment</comments>
      <pubDate>Sat, 5 Jul 2025 00:41:28 +0900</pubDate>
    </item>
  </channel>
</rss>