목차

  • [[#vector|vector]]
  • [[#array|array]]
  • [[#List|List]]
  • [[#Deque|Deque]]
  • [[#Set|Set]]
  • [[#Map|Map]]
  • [[#컨테이너 어댑터|컨테이너 어댑터]]

@주의 : 기본적으로 컨테이너는 객체 로 얇은 복사(shallow copy)

vector

  • 동적 배열 사용

  • 주요 동작 방식

    • 동적 크기 조정: 벡터의 크기가 증가하면, 현재 배열의 두 배 크기의 새로운 메모리 블록을 할당하고 기존 데이터를 새로운 블록으로 복사합니다. <- 지수적 크기 증가방식 사용
    • 상수 시간 접근: 연속된 메모리 블록을 사용하므로, 임의의 위치에 있는 요소에 대한 접근은 상수 시간($O(1)$)이 소요됩니다.
    • 배열과 유사한 성능: 벡터는 일반 배열처럼 작동하지만, 크기가 동적으로 조정된다는 점에서 배열과 다릅니다.
    • 효율적인 삽입 및 삭제: 벡터의 끝 부분에서 삽입 및 삭제는 평균적으로 상수 시간($O(1)$)이 소요됩니다. 하지만, 중간이나 시작 부분에서의 삽입 및 삭제는 이동 작업이 필요하기 때문에 선형 시간($O(n)$)이 소요됩니다.
  • 수동 메모리해제

      vec.clear(); // 벡터의 모든 요소 제거
      vec.shrink_to_fit(); // 벡터의 용량을 현재 크기로 줄임

array

  • vector의 생성, 소멸 성능이슈로 만들어진 클래스
  • 동적 크기 변경 기능 x
  • 정적 크기:
    • 배열 크기는 컴파일 타임에 고정되어야 합니다. 런타임에 크기를 변경할 수 없습니다.
  • STL 호환:
    • 범위 기반 for 루프, STL 알고리즘, 이터레이터 등이 지원됩니다.
  • 추가 메서드 제공:
    • 배열의 크기 확인(.size()), 요소에 안전하게 접근(.at()), 내부 데이터 접근(.data()) 등을 지원합니다.
  • C 스타일 배열과 호환:
    • 기존 C 배열처럼 사용할 수 있으며, .data()를 통해 포인터로 변환 가능합니다.

List

  • 이중 연결 리스트:
    • 각 요소는 앞뒤로 연결된 노드를 가지며, 양방향으로 순회가 가능합니다.
    • 삽입과 삭제가 O(1)의 시간 복잡도를 가집니다(특정 위치에서).
  • 임의 접근(random access)이 불가능:
    • 인덱스를 통한 접근(list[i])을 지원하지 않으며, 순차적으로 순회해야 합니다.
    • 따라서, O(N)의 시간 복잡도로 특정 위치의 요소를 검색합니다.
  • 효율적인 삽입 및 삭제:
    • 배열(vector)처럼 요소를 이동시키지 않고, 포인터만 변경하므로 삽입/삭제가 매우 빠릅니다.
    • 특히, 중간 위치에서의 삽입/삭제가 빈번할 때 적합합니다.

Deque

  • 양방향 삽입/삭제:
    • 양쪽 끝(프론트와 백)에서 요소를 삽입하거나 삭제할 수 있습니다.
    • 이는 std::vector와의 주요 차이점 중 하나입니다.
  • 연속적 메모리 할당은 아님:
    • std::deque는 내부적으로 연속적 메모리 블록으로 구성되지 않습니다.
    • 대신 여러 개의 고정 크기 메모리 블록을 연결하여 요소를 저장합니다.
    • 따라서 중간에 메모리를 이동시키지 않고도 삽입/삭제가 가능합니다.
  • 임의 접근 지원:
    • std::vector와 마찬가지로, [] 연산자 및 .at() 메서드를 사용하여 O(1) 시간 복잡도로 임의 요소에 접근할 수 있습니다.
  • 범용성과 성능:
    • std::deque는 양 끝 삽입/삭제가 빈번한 경우에 적합합니다.
    • 하지만 중간에 삽입/삭제가 빈번하다면 std::list가 더 적합할 수 있습니다.

Set

  • 내부적으로 이진 검색 트리(BST, 보통 Red-Black Tree)로 구현
  • 자동 정렬:
    • 요소는 삽입될 때 자동으로 정렬됩니다(기본적으로 오름차순).
  • 중복 방지:
    • 같은 값을 여러 번 삽입하려고 하면 하나의 값만 저장됩니다.
  • 탐색:
    • 이진 검색 트리를 기반으로 하여 O(log N)의 시간 복잡도로 탐색할 수 있습니다.
  • multiset :
    • 중복 원소 허락
    • equal_range를 통해, 속하는 iterator범위를 얻을 수 있음
  • unordered_set :
    • 삽입, 삭제, 검색이 O(1) 수행
      • 해시 함수 사용

Map

  • 키-값 쌍(key-value pair)을 저장하는 연관 컨테이너입니다.
  • 키는 유일해야 하며, 각 키에 대해 하나의 값이 매핑됩니다.
  • 내부적으로 std::set처럼 이진 검색 트리(BST)를 사용합니다.
  • 정렬된 저장:
    • std::map의 모든 키는 자동으로 정렬됩니다.
  • 키를 통한 빠른 검색:
    • 특정 키를 사용해 O(log N)의 시간 복잡도로 값을 검색할 수 있습니다.
  • 중복된 키 금지:
    • 동일한 키를 두 번 삽입하면 덮어쓰기
  • multimap :
    • 중복 원소 허락
    • equal_range를 통해, 속하는 iterator범위를 얻을 수 있음
  • unordered_map :
    • 삽입, 삭제, 검색이 O(1) 수행
      • 해시 함수 사용
      • 최악의 경우 O(n)

컨테이너 어댑터

  • C++ STL에서 제공되는 일련의 템플릿 클래스
  • 기존의 순차 컨테이너(vector, list 등)을 기반으로 한 새로운 인터페이스 제공, 특정 자료 구조의 특성 구현 도움
  • 대표 컨테이너 어뎁터 : 스택, 큐, 우선 순위 큐

컨테이너 분류 표

분류 컨테이너 설명
순차 컨테이너 vector, deque, list, forward_list, array - 순서대로 요소를 저장합니다.
- 배열이나 연결 리스트 형태로 구현됩니다.
연관 컨테이너 map, multimap, set, multiset - 키와 값으로 데이터를 저장하거나 정렬된 데이터를 관리합니다.
- 요소들이 자동으로 정렬됩니다.
비정렬 연관 컨테이너 unordered_map, unordered_multimap, unordered_set, unordered_multiset - 정렬되지 않은 데이터를 관리합니다.
- 해시 테이블 기반으로 빠른 검색을 지원합니다.
컨테이너 어댑터 stack, queue, priority_queue - 기존 컨테이너를 기반으로 특수한 동작(스택, 큐, 우선순위 큐 등)을 제공합니다.
- 기본적으로 deque가 내부 컨테이너로 사용됩니다.

'공부 > C++' 카테고리의 다른 글

디버그 출력  (0) 2025.03.02
C++ 저장&링킹  (1) 2025.03.01
new&delete  (0) 2025.03.01
포인터  (0) 2025.02.22
Operator 기본 연산자  (0) 2025.02.21

참고 :new 및 delete 연산자 | Microsoft Learn)

new 연산자

  • 할당 요청에 대한 메모리가 불충분한 경우, std::bad_alloc예외를 throw || new(std::nothrow) 사용
  • throw되지 않는 operator new 연결의 경우, nullptr 반환
  • 반환 값은 void*
  • 2 가지 범위
    • ::operator new : 전역
    • class-name::operator new : 클래스
  • **기본 제공 형식 개체, 사용자 정의된 operater new 함수를 포함하지 않느 클래스 형식 개체 및 모든 형식 "배열" 할당하는 데 new 연산자 사용하면, 전역 new 함수 호출
  • 클래스 범위의 new 함수는 전역 new함수를 숨기는 정적 멤버 함수.
    • 클래스 new에서 size_t 파라미터를 요구해도, Blanks *SomeBlanks = new Blanks; 같은 방식의 오류 생성 코드가 발생 가능

nothrow를 이용한 검사

  • nullptr을 반환하게 할 수 있어 검사 용이
    #include <iostream>
    #include <new>
    using namespace std;
    #define BIG_NUMBER 10000000000LL
    int main() {
     int *pI = new(nothrow) int[BIG_NUMBER];
     if ( pI == nullptr ) {
        cout << "Insufficient memory" << endl;
        return -1;
     }
    }

delete 연산자

  • 2가지 양식
    • size_t양식은 크기 범주를 빠르게 검색, 파생 클래스 제거에 용이
      void operator delete( void * );
      void operator delete( void *, size_t );
  • 반환 값은 void

'공부 > C++' 카테고리의 다른 글

C++ 저장&링킹  (1) 2025.03.01
C++ 컨테이너 클래스  (0) 2025.03.01
포인터  (0) 2025.02.22
Operator 기본 연산자  (0) 2025.02.21
C++ main()에 대해  (0) 2025.02.19

참고 : jinh2352_데이터의존성, 두 변수 값 바꾸기에 대한 고찰: 후속편

데이터 의존성(Data Dependency)

  • 명령어들 사이의 데이터 흐름
    • 의존성 있는 명령은 순서대로 실행되어야 함
      -> 최적화 시 고려 필수 고려
  • 데이터 헤저드(Hazard) : 컴퓨터 구조에 한정하여, 이러한 의존성을 말함.

RAW (Read after Writer) 의존성

  • 흐름 의존성(flow dependency), 진짜 의존성 (true dependency)
  • 1번 명령이 계산 결과 생성
  • 2번 명령이 사용
  • @ : x기준, 2번 명령은 1번 명령이 끝이 나야 사용 가능.
    • x의 값에 대해 Write -> Read 과정
      x = y + 1; // (1) x에 대한 값 쓰기
      z = x * 2; // (2) x에 대한 값 읽기
  • XOR-Swap 방식이 최적화가 temp방식 보다 어려운 이유

WAR (Write after Read)

  • 반 의존성 (anti dependency), 가짜 의존성 (false dependency), 이름 의존성 (name dependency)
  • 1번 명령에서 x 값 읽기
  • 2번 명령에서 x 값 쓰기
  • @ : x 기준, 2번 명령은 1번 명령과 동시 || 순서 변경에도 문제가 없음
    z = x + 2; // (1)
    x = y + 1; // (2) <- (1) 번 명령은 (2)와 관련이 없음
    a = x / 2;
  • 의존성 제거

    • (2) 번 명령의 x의 이름을 x1 등의 다른 이름으로 변경
      z = x + 2; 
      x1 = y + 1; // 이후 `x`들도 전부 `x1`로 이름 변경 필요
      a = x1 / 2;

WAW (Write after Write)

  • 출력 의존성 (output dependency), 가짜 의존성 (false dependency), 이름 의존성 (name dependency)
  • 같은 대상 사용에 따른 의존성
  • @ : WAR과 달리, 순서도 중요
    x = z + 1;  // (1)
    x = y + 3;  // (2)
    a = x / 2;
  • 의존성 제거

    • (2) 번 명령의 x의 이름을 x1 등의 다른 이름으로 변경
      x = z + 1;
      x1 = y + 3;
      a = x1 / 2;

'공부 > CS' 카테고리의 다른 글

C++ 과 C# 의 차이  (0) 2024.05.22
데이터 계층  (0) 2023.06.28
파이프라인 데이터페스  (0) 2023.06.28
MIPS 데이터 패스 구동 방식  (0) 2023.06.28
MIPS 주소 방식  (0) 2023.06.28

참고 : [IT Series] 2진수 4칙연산(더하기,빼기,곱하기,나누기) 원리

덧셈

  • 자릿수(carry) + 덧셈 (xor) 로 표현 가능
    int bitwise_add(int a, int b) {
      while (b != 0) {  // 더이상 자리올림 값이 없을 떄까지 반복
          int carry = a & b;  // 자리올림 계산
          a = a ^ b;          // 자리올림을 제외한 덧셈
          b = carry << 1;     // 자리올림을 왼쪽으로 한 비트 이동
      }
      return a;
    }

뺄셈

  • 2의 보수를 계산하고 덧셈 과정 진행
    int bitwise_subtract(int a, int b) {
      // b의 2의 보수 계산
      int b_complement = ~b + 1;
      // a와 b의 2의 보수 더하기
      return a + b_complement;
    }

곱셈

  • 더하기로 분리하여 계산
  • 마지막 bit가 1이면, 결과값 더하기.
  • 곱하는 값은 >> 연산을 통해 더한 부분 제거
  • 곱해지는 값은 << 연산을 통해 자릿수 올리기
    int bitwise_multiply(int a, int b) {
      int result = 0;
      while (b != 0) {
          if (b & 1) { // b의 마지막 비트가 1인 경우
              result = result + a;
          }
          a <<= 1; // a를 왼쪽으로 시프트
          b >>= 1; // b를 오른쪽으로 시프트
      }
      return result;
    }

나눗셈

  • 빼기로 분리하여 계산

    • 피제수의 부호를 확인하고 양수 변환

    • 피제수를 >> 연산해서 앞자리부터 값이 제수보다 큰지 비교

    • 크다면 몫에 해당 비트 위치를 더하고,

    • 피제수에 제수를 해당 비트 위치만큼 << 연산한 값을 뺌

    • 자릿수 만큼 반복 (마지막 피제수는 나머지 값이 됨)

      int bitwise_divide(int dividend, int divisor) {
      if (divisor == 0) {
        throw std::invalid_argument("Divisor cannot be zero.");
      }
      
      int quotient = 0;
      int remainder = 0;
      
      // 피제수의 부호를 확인하고 양수로 변환
      bool is_negative = (dividend < 0) ^ (divisor < 0);
      dividend = abs(dividend);
      divisor = abs(divisor);
      
      for (int i = 31; i >= 0; --i) {
        // divisor를 왼쪽으로 i만큼 시프트하여 dividend와 비교
        if ((dividend >> i) >= divisor) {
            // quotient에 해당 비트 위치를 더함
            quotient += (1 << i);
            // dividend에서 시프트된 divisor를 뺌
            dividend -= (divisor << i);
        }
      }
      
      // 몫의 부호를 결정
      return is_negative ? -quotient : quotient;
      }


'공부 > 알고리즘공부' 카테고리의 다른 글

Counting Sort  (0) 2025.04.29
Radix Sort  (0) 2025.04.29
정렬알고리즘 #단순정렬  (0) 2022.07.11
In-place & Stable ?  (0) 2022.05.01
Two Point 알고리즘  (0) 2022.04.29

4.1 기본기

4.1.1 Direct3D 12 개요

  • 정의 : 응용프로그램에서 GPU를 제어하고 프로그래밍 하는데 쓰는 저수준 그래픽 API
  • 책에서 "렌더 대상을 지운다" = 대상의 모든 원소를 특정한 하나의 값으로 설정한다. (delete, remove가 아님)
    • ID3D12CommandList::ClearRenderTargetView 메서드 호출
  • 응용 프로그램 - 그래픽 하드웨어 사이, Direct3D라는 간접층과 하드웨어 드라이버가 Direct3D 명령들을 기계어 명령들로 번역.
    • 응용 프로그램 개발자는 GPU 세부사항 걱정x
    • NIVIDIA, Intel, AMD 제조사들이 Direct3D의 명세를 준수하는 드라이버 제공 필요
  • D11과 차이점:
    • 새로운 렌더링 기능 추가
    • CPU 부담 감소 및 다중 스레드 지원
    • GPU에 더 가까운 수준의 API
    • 추상화 감소 및 개발자가 관리해야 할 사항 증가
    • 현세대 GPU 구조 밀접 반영
    • API 사용 난이도 증가 및 성능 개선
  • 여담 :
    • D12는 하드웨어 관리가 늘어나면서, D13 개발보다는 기능 추가가 되고 있음.

4.1.2 COM

  • COM (Component Object Model)
    • DirectX 프로그래밍 언어 독립성과 하위 호환성을 지원
    • c++로 DirectX 응용 프로그램을 프로그래밍 할 때, 'COM'의 세부 사항은 대부분 드러나지 않음
      • 'COM 인터페이스'를 new 키워드로 직접 생성할 일 없음
        • 프로그래머는 필요한 'COM 인터페이스'를 가리키는 포인터를 특별한 함수들을 이용 or
        • 다른 'COM'인터페이스의 메서드를 얻는 방법
    • 'COM 인터페이스'의 삭제는 delete가 아니라 인터페이스의 Release 메서드 호출
      • 모든 'COM 인터페이스'는 IUnknown 인터페이스 기능 상속, 해당 인터페이스는 Release 메서드 제공
      • 참조 횟수가 0이 되면 메모리에서 해제
    • 참고 : 'COM 인터페이스'의 이름은 대문자 I로 시작.
  • Microsoft::WRL::ComPtr <- "wrl.h" 필요
    • Com객체의 수명 관리를 돕는, Smart Pointer
    • 범위를 벗어난 ComPtr 인스턴스는 바탕 Com 객체에 대해 자동으로 Release 호출
      • (책에서 사용하는 메서드)
      • Get : 바탕 COM 인터페이스를 가리키는 포인터 반환.
          ComPtr<ID3D12RootSignature> mRootSignature;
          ...
          // SetGraphicsRootSignature는 ID3D12RootSignature* 형식의 인수를 받음
          mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
      • 해당 COM 인터페이스 포인터 형식의 인수를 받는 함수를 호출할 떄 사용
      • GetAddressOf : 바탕 COM 인터페이스를 가리키는 포인터의 주소를 반환.
        함수의 매개변수를 통해 포인터를 돌려받을 떄 흔히 사용
      • ComPtr<ID3D12CommandAllocator> mDirectCmdListAlloc; ... ThrowIfFailed(md3dDevice->CreateCommandAllocator( D3D12_COMMAND_LISTTYPE_DIRECT, mDirectCmdListAlloc.GetAddressOf()));
      • Reset: ComPtr : 인스터를 nullptr로 설정하고 바탕 COM 인터페이스의 참조 횟수를 1 감소
        • 직접 ComPtr 인스턴스에 nullptr 배정해도 됨
        • ComPtr<ID3D12Device> device = nullptr;

4.1.3 텍스쳐 형식

  • n차원 텍스쳐 = n차원 배열
  • 텍스쳐는 특정 형식의 자료 원소들만 담을 수 있음
    • DXGI_FORMAT 이라는 열거형으로 지정
      • DXGI_FORMAT_R32G32B32_FLAOT: 각 원소는 32bit 부동 소수점 성분 3 개로 구성
      • DXGI_FORMAT_R16G16B16A16_UNORM: 각 원소는 [0, 1] 구간으로 사상되는 16bit 성분 4 개로 구성
      • DXGI_FORMAT_R32G32_UINT: 각 원소는 부호없는 32bit 정수 성분 2 개로 구성
      • DXGI_FORMAT_R8G8B8A8_UNORM: 각 원소는 [0, 1] 구간으로 사상되는 부호 없는 8bit 성분 4 개로 구성
      • DXGI_FORMAT_R8G8B8A8_SNORM: 각 원소는 [-1, 1] 구간으로 사상되는 부호 있는 8bit 성분 4 개로 구성
      • DXGI_FORMAT_R8G8B8A8_SINT: 각 원소는 [-127, 127] 구간으로 사상되는 부호 있는 8bit 정수 성분 4 개로 구성
      • DXGI_FORMAT_R8G8B8A8_UINT: 각 원소는 [0, 255] 구간으로 사상되는 부호 없는 8bit 정수 성분 4 개로 구성
      • DXGI_FORMAT_R16G16B16A16_TYPELESS: 각 비트는 설정하지만, 타입을 지정하지 않음 <- 메모리만 확보, 추후 텍스처를 파이프라인에 묶을 때 지정하는 용도로 사용('c++'의 'reinterpret_cast'와 유사
        • reinterpret_cast : c++에서 다양한 타입 값 변환할 때 사용되는 연산자
      • -> DXGI_FORMAT_{RGBA_bit 표시}_{타입} 형태의 직관적 이름
      • -> [-127,1277], [0, 255]의 구간 차이로 인해, 색깔 값의 표현이 다소 달라짐

4.1.4 교환 사슬과 페이지 전환

  • 이중 버퍼링(Double Buffering) :
    • 후면 버퍼에서 그림을 그리고 전면 버퍼로 완성된 그림을 송출
    • 사용자는 완성된 그림 만을 보게 됨.
    • 버퍼 스왑(Buffer Swap) :
      • 후면 버퍼가 완전히 렌더링 되면, 후면 버퍼와 전면 버퍼의 역할이 서로 바뀜
      • 전면 버퍼와 후면 버퍼는 하나의 교환 사슬(swap chain)을 형성
        • IDXGISwapChain :
          • 전면 버퍼 픽셀 데이터와 후면 버퍼 텍스처를 담음
          • IDXGISwapChain::ResizeBuffers : 버퍼 크기 변경
          • IDXGISwapChain::Present : 버퍼의 *"제시"*
      • D3D에서는 제시(presenting) 라고 부름
    • 전면 버퍼(Front Buffer) :
      • 저장된 내용 표시
      • 픽셀(Pixel) 단위 데이터 처리
    • 후면 버퍼 (Back Buffer) :
      • 그래픽 데이터가 먼저 렌더링 되는 곳
      • 그래픽 연산 수행
      • 현재 화면에 표시x
      • 텍셀(Texel) 단위 데이터 처리
        • 하지만, 픽셀 데이터 저장이 주된 역할
    • 장점 :
      • 화면 깜빡임 방지: 그래픽 데이터를 렌더링하는 동안 깜박임 현상 방지
      • 티어링 방지: 화면이 잘못된 위치에서 찢어지는 티어링 방지
         -> 프레임 버퍼와 모니터 동기화에서 발생하는 문제, 이전 프레임과 섞이는 현상으로 수직, 수평 방향으로 어긋남 발생; (정점 버퍼와 관계 x)

4.1.5 깊이 버퍼링

  • 깊이 버퍼(Detph Buffer) :
    • 이미지 자료를 담지 않는 텍스처
    • 각 픽셀의 깊이 정보 보관
      • 깊이는 0..0 ~ 1.0
        • 0.0 : 시야 절두체 안에서 관찰자와 최대한 가까운 물체
        • 1.0 : 시야 절두체 안에서 관찰자와 최대한 먼 물체
        • 시야 절두체(view frustum)
          • 3D 그래픽스에서 카메라가 볼 수 있는 공간을 정의
          • 구성
            • 근평면 (Near Plane): 카메라와 가장 가까운 절두체의 면으로, 화면에 표시되는 최소 거리입니다.
            • 원평면 (Far Plane): 카메라와 가장 먼 절두체의 면으로, 화면에 표시되는 최대 거리입니다.
            • 상면 (Top Plane): 절두체의 윗면으로, 카메라의 시야 각을 정의합니다.
            • 하면 (Bottom Plane): 절두체의 아랫면으로, 카메라의 시야 각을 정의합니다.
            • 좌면 (Left Plane): 절두체의 왼쪽 면으로, 카메라의 시야 각을 정의합니다.
            • 우면 (Right Plane): 절두체의 오른쪽 면으로, 카메라의 시야 각을 정의합니다.
    • 깊이 버퍼의 원소들과 후면 버퍼의 픽셀들은 일대일 대응
      • ex) 1080 x 1024 해상도는 1280 x 1024개 원소 깊이 버퍼로 구성
    • 깊이 버퍼는 하나의 텍스처
      • 생성 시 특정한 자료 원소 형식 지정 필요
      • ex)
        • DXGI_FORMAT_D32_FLOAT_S8X24_UINT : 각 텍셀은 32비트 부동소수점 깊이 값과 [0,255] 구간으로 사상되는 부호 없는 8bit 정수 스텐실 값, 그리고 다른 용도 없이 채움(padding)용 24bit 구성
        • DXGI_FORMAT_D32_FLOAT : 각 텍셀은 32비트 부동소수점 깊이 값
        • DXGI_FORMAT_D24_FLOAT_S8_UINT : 각 텍셀은 [0,1]구간으로 사상되는 부호 없는 24bit 깊이 값 1개, [0,255]구간 사상되는 부호 없는 8bit 정수 스텐실 값
        • DXGI_FORMAT_D16_UNORM : 각 텍셀은 [0,1] 구간으로 사상되는 부호 없는 16bit 깊이 값
          • -> 응용 프로그램이 스텐실 버퍼를 반드시 사용해야 하는 것은 아니나, 사용한다면 스텐실 버퍼는 항상 깊이 버퍼와 같은 텍스처에 포함
  • "D3D"는 깊이 버퍼링 또는 z-버퍼링 사용
    • 깊이 버퍼링 : 그리는 순서 무관하게 물체들이 제대로 가려짐
      • (z-버퍼링과 깊이 버퍼링은 같은 개념 지칭)
    • 깊이 판정 실패 시, 버퍼 갱신 x
      • 가장 앞에 있는 물체보다 뒤에 물체가 추가 될 떄, 버퍼 갱신을 하지않음

4.1.6 자원과 서술자

  • 그리기 호출
    • 호출 전에 필요한 자원을 바인딩 시킬 필요가 있다
    • 필요하다면 호출마다 바인딩 갱신
    • GPU 자원들이 파이프 라인에 직접 묶이는 것 X
      • 자원 서술자를 통해 실제 자료 접근
    • 호출이 참조할 서술자 명시 -> 해당 자원들이 렌더링 파이프라인에 바인딩
  • 서술자(Descriptor) : { <- "View"는 서술자와 동의어 }
    • 자원을 GPU에게 서술해 주는 경량 자료구조
    • 서술자는 간접층(level of indirection)
      • GPU 자원이 사실상 범용적인 메모리 조각이기 때문
    • 응용프로그램 초기화 시점에 생성해야 함
      • 해당 시점에 일정 정도의 형식 점검과 유효성 검증이 일어나기 떄문
      • 초기화 시점에서 생성하는 것이 실행 시점보다 나음
        • 최적화, 안정성(디버깅), 반복 재사용, 메모리관리, 서술자 힙 구조 적합성 등으로 인해
    • 서술자 종류 일부 (책에 사용하는 것들)
      • Shader Resource View(SRV) : 텍스처의 특정 MIP 레벨이나 배열의 특정 슬라이스를 참조
      • Constant Buffer View (CBV) : 상수 버퍼의 특정 범위를 참조
      • Unordered Access View (UAV) : 버퍼나 텍스처의 특정 영역에 쓰기 작업 수행
      • 표본추출기 서술자 : 텍스처 적용에 쓰이는 표본추출기(sampler) 자원 서술
      • Render Target View(RTV) : 렌더 대상 자원 서술
      • Depth Stencil View(DSV) : 깊이/스텐실 자원 서술
    • 자원
      • 범용적
      • 같은 자원을 렌더링 파이프라인의 서로 다른 단계(stage)에 사용 가능
        • ex) 텍스처 : 렌더 대상 사용 -> 셰이더 자원으로도 사용
        • 단계마다 개별적인 서술자 필요
      • 사용처에 대한 명시 x
        • 서술자가 지정 및 GPU에 서술해줄 필요가 있음
      • 부분 영역 정보 x
        • 자원의 일부 영역만 렌더링 파이프라인에 묶이는게 불가
        • 서술자가 부분 영역을 지정해줄 수 있음
          • SRV, CBV, UAV
      • 무형식으로 생성 가능
        • GPU는 자원 형식 파악 불가
        • 해당 자원을 참조하는 서술자를 생성할 떄, 구체적인 형식 명시 가능
        • 런탐임에 자원에 대한 접근 최적화를 위해, 가능한 지양
  • 서술자 힙(Descriptor heap)
    • 서술자 배열
    • 서술자 종류마다 개별적인 힙 필요
      • 같은 종류는 같은 힙에 저장 ( 한 종류에 여러개 힙도 가능 )

4.1.7 다중표본화의 이론

  • 앨리어싱 제거(antialiasing) 기법
    • 초과표본화(supersampling) :
      • 후면 버퍼와 깊이 버퍼를 화면 해상도보다 4배 (가로 2, 세로2) 크게 잡고, 렌더링. -> 제시할 떄, 버퍼를 원래 크기로 환원(resolving)
        • 하양표본화(downsampling) :
          • 4pixel 블록의 네 생상의 평균을 최종 색상으로 지정하는 환원 공정
          • 소프트웨어에서 해상도를 늘려 해결하는 방식
      • 4배로 늘렸기에 비용이 높음
    • 다중표본화(multisampling)
      • 절충적인 앨리어싱 제거 기법 (D3D에서 사용)
      • 일부 계산 결과를 부분픽셀(subpixel)들 사이서 공유 -> 비용 저렴
      • supersampling 처럼 x배인 후면 버퍼와 깊이 버퍼 사용
        • 픽셀당 한 번만 색상 계산 -> 해당 색상과 부분픽셀들의 가시성, 포괄도를 이용해 최종 결정

4.1.8 Direct3D의 Multisampling

  • DXGI_SAMPLE_DESC 구조체를 적절히 채워야 함
    •     typedef struct DXGI_SAMPLE_DESC
          {
              UINT Count;      // 추출할 표본 개수
              UINT Quality     // 원하는 품질 수준
          } DXGI_SAMPLE_DESC;
    • 표본_개수&품질_수준은 "렌더링 비용"과 정비례
    • 교환 사슬 버퍼, 깊이 버퍼 모두 필요
    • 후면 버퍼와 깊이 버퍼를 생성할 떄, 동일한 multisampling 설정 적용
  • 텍스처 형식과 표본 개수이 조합에 대한 품질 수준 개수 파악 메서드 사용 예
      typedef struct D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS {
          DXGI_FORMAT            
          Format;
          UINT                    
          SampleCount;
          D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG Flags;
          UINT                    
          NumQualityLevels;
      } D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS;
    
      D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
      msQualityLevels.Format = mBackBufferFormat;
      msQualityLevels.SampleCount = 4;
      msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
      msQualityLevels.NumQualityLevels = 0;
      ThrowIfFailed(md3dDevice->CheckFeatureSupport(
          D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
          &msQualityLevels, sizeof(msQualityLevels)));
    ```</div></details>
  • 실제 응용에서 multisampling 표본을 4~8개만 추출
  • D3D12 대응 장치는 모든 렌더 대상 형식에 대해 4x multisampling 지원
    • Count 1, Quality 0 설정하면 multisampling 사용 x

4.1.9 기능수준

  • D3D_FEATURE_LEVEL 열거형
    • GPU가 지원하는 기능들의 엄격한 집합

4.1.10 DXGI (DirectX Graphics Infrastructure)

  • D3D와 함께 쓰이는 API
    • IDXGISwapChain
    • 화면 전환 등등
    • <- IDXGI{Interface}{Ver} 을 쓰기 위해서는 `<dxgi{Ver}>` 헤더 필요 <- 헤더 링크 설정 필요 `#pragma comment(lib, "dxgi.lib"`>
  • IDXGIFactory : DXGI를 사용하기 위해 필요한 각종 인터페이스 생성
  • IDXGIAdapter : 그래픽카드 열거
  • IDXGIOutput : 그래픽 카드에 연결된 디스플레이 기능 제어
  • IDXGISwapChain : double buffering, triple buffering 에 사용
    • 시스템 모든 디스플레이 어댑터 열거
        //시스템의 모든 디스플레이 어댑터를 열거한다.
        void D3DApp::LogAdapters()
        {
          // DXGI Factory 생성
          ComPtr<IDXGIFactory4> mdxgiFactory;
          if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory))))
                  return -1;
      
            UINT i = 0;
            IDXGIAdapter*  adapter = nullptr;
            std::vector<IDXGIAdapter*> adapterList;
            while (mdxgiFactory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND)
            {
                DXGI_ADAPTER_DESC desc;
                adapter->GetDesc(&desc);
      
                std::wstring text = L"***Adapter";
                text += desc.Description;
                text+= L'\n';
      
                OutputDebugString(text.c_str());
      
                adapterList.push_back(adapter);
      
                ++i;
            }
      
            for (size_t i = 0; i < adapterList.size(); ++i)
            {
                LogAdapterOutputs(adapterList[i]);
            }
        }
      ```</div></details>
    • 어댑터 관련 모든 출력 열거
        //주어진 어댑터와 연관된 모든 출력을 열거한다.
        void D3DApp::LogAdapterOutputs(IDXGIAdapter* adapter)
        {
            UINT i = 0;
            IDXGIOutput* output = nullptr;
            while (adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND)
            {
                DXGI_OUTPUT_DESC desc;
                output->GetDesc(&desc);
      
                std::wstring text = L"***Output : ";
                text += desc.DeviceName;
                text += L"\n";
                OutputDebugString(text.c_str());
      
                LogOutputDisplayModes(output, mBackBufferFormat);
      
                ReleaseCom(output);
      
                ++i;
            }
        }
      ```</div></details>
    • DXGI_MODE_DESC 해당 형태 구조체들은 디스플레이 관련 정보를 담는다.
    • tytypedef struct DXGI_MODE_DESC { UINT Width; // 가로 해상도 UINT Height; // 세로 해상도 DXGI_RATIONAL RefreshRate; // 주사율 DXGI_FORMAT Format; // 디스플레이 형식 DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; //스캔 방식 : 순차 주사(프로그레시브) or 비월 주사(인터레이스) DXGI_MODE_SCALING Scaling; // 영상 모니터 크기에 맞게 스케일 값 } ```
    • 주어진 출력과 디스플레이 형식을 지원하는 모든 디스플레이 모드 목록 출력

      ```
      주어진 출력과 픽셀 형식의 조합이 지원하는 모든 디스플레이 모드를 나열한다.
      void D3DApp::LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format)
      {

        UINT count = 0;
        UINT flags = 0;
      
        //nullpter을 인수로 해서 호출하면 목록의 크기(모드 개수)를 얻게 된다.
        output->GetDisplayModeList(format, flags, &count, nullptr);
      
        std::vector<DXGI_MODE_DESC> modeList(count);
        output->GetDisplayModeList(format, flags, &count, &modeList[0]);
      
        for (auto& x : modeList)
        {
            UINT n = x.RefreshRate.Numerator;
            UINT d = x.RefreshRate.Denominator;
            std::wstring text =
                L"Width = " + std::to_wstring(x.Width) + L" " +
                L"Height = " + std::to_wstring(x.Height) + L" " +
                L"Refresh = " + std::to_wstring(n) + L"/" + std::to_wstring(d) +
                L"\n";
      
            ::OutputDebugString(text.c_str());
      
        }

      }
      ```

      • 전체 화면 성능 최적화를 위해 지원 모니터 지정에 유용
    • 위의 예제 코드 실행 시, 필요 전처리문
                  #pragma comment(lib, "dxgi.lib")
      
                  #include <dxgi1_4.h>// IDXGI_
                  #include <wrl.h>    // Microsoft::WRL::ComPtr
                  #include <vector>
                  #include <iostream>
                  #include <string>    // std::to_string
      
                  #ifndef ReleaseCom
                  #define ReleaseCom(x) { if(x){ x->Release(); x = 0; } }
                  #endif
      ```</div></details>
      
      

4.1.11 기능지원점검

  • CheckFeatureSupport
  • HRHRESULT ID3D12Device::CheckFeatureSupport( D3D12_FEATURE Feature, void *pFeatureSupportData, UINT FeatureSupportDataSize );
  • Feature : 지원 여부 점검할 기능 종류
    • D3D12_FEATURE_D3D12_OPTIONS : D3D 12의 여러 기능
    • D3D12_FEATURE_ARCHITECTURE : 하드웨어 아키텍처 기능들
    • D3D12_FEATURE_FEATURE_LEVELS : 기능 수준들
    • D3D12_FEATURE_FORMAT_SUPPORT : 텍스처 형식에 대한 기능
    • D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS : 다중 표본화 기능
  • pFeatureSupportData : 기능 지원 정보 설정될 구조체 포인터
    • Feature 의 타입과 동일해야 함
  • FeatureSupportDataSize : pFeatureSupportData 에 전달한 구조체 크기
  • 다중 기능 지원 점검 예제
      typedef struct D3D12_FEATURE_DATA_FEATURE_LEVELS 
      {
              UINT NumFeatureLevels;
              const D3D_FEATURE_LEVEL
              *pFeatureLevelsRequested;
              D3D_FEATURE_LEVEL   MaxSupportedFeatureLevel; 
          } D3D12_FEATURE_DATA_FEATURE_LEVELS;
          D3D_FEATURE_LEVEL featureLevels[3] =
      {
          D3D_FEATURE_LEVEL_11_0, // First check D3D 11 support
          D3D_FEATURE_LEVEL_10_0, // Next, check D3D 10 support
          D3D_FEATURE_LEVEL_9_3  // Finally, check D3D 9.3 support
      };
      D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevelsInfo;
      featureLevelsInfo.NumFeatureLevels = 3;
      featureLevelsInfo.pFeatureLevelsRequested = featureLevels;
      md3dDevice->CheckFeatureSupport(
          D3D12_FEATURE_FEATURE_LEVELS,
          &featureLevelsInfo,
          sizeof(featureLevelsInfo));
    ```</div></details>
    
    

4.1.12 상주성

  • 상주성(regsidency) :
    • 응용 프로그램 자원을 GPU메모리에 내리('퇴거')거나 올림('입주')으로 자원의 상주성을 관리
    • GPU 메모리 양 최소화가 핵심
    • 상주성 변경의 적합한 시점 예 : "게임 레벨", "게임 지역" 변화 시점
    • 기본적으로 자원 생성 -> 입주, 파괴 -> 퇴거
    • MakeResident : 자원을 GPU 메모리에 유지
        HRESULT MakeResident(
        UINT NumObjects,
        ID3D12Pageable *const *ppObjects
      ));
    • Evict : 자원을 GPU 메모리에서 내보냄
        HRESULT Evit(
            UINT NumObjects,
            ID3D12Pageable *const *ppObjects
        );

'프레임워크 > DirectX' 카테고리의 다른 글

Direct3D의 초기화_게임프레임워크&타이머 코드 의문점  (0) 2025.03.07
04.Direct3D의_초기화03  (1) 2025.03.04
04.Direct3D의_초기화02  (0) 2025.03.04
02.Matrix  (0) 2025.02.25
01.Vector  (0) 2025.02.25

2.1 행렬

  • m x n 로 이루어진 실수들의 정사각 배열 _ $M_{mn}$
  • 행렬의 차원 : 행 x 열 개수의 곱(4 * 4 같은)
  • 원소(element) : 행렬을 구성하는 수
  • 행 벡터, 열 벡터 : 각각 행과 열이 하나 인 행렬
  • 속성
    • 대응되는 성분들이 상등일 때에만 상등
      • 비교하려면, 두 행렬의 차원이 같아야 함
    • 행렬 덧셈은 대응되는 성분들을 더함.
    • 하나의 스칼라 값을 곱할 때, 모든 성분에 곱 함.
    • 뺼셈은 스칼라 곱과 덧셈으로 정의 (A-B = A+(-1 $\cdot$ B))

2.2 행렬 곱셈

  • $$ \mathbf{C} = \mathbf{A} \mathbf{B} = \begin{pmatrix}1 & 2 \\ 3 & 4\end{pmatrix} \begin{pmatrix}5 & 6 \\ 7 & 8\end{pmatrix} = \begin{pmatrix}1 \cdot 5 + 2 \cdot 7 & 1 \cdot 6 + 2 \cdot 8 \\ 3 \cdot 5 + 4 \cdot 7 & 3 \cdot 6 + 4 \cdot 8\end{pmatrix} = \begin{pmatrix}19 & 22 \\ 43 & 50\end{pmatrix} $$
  • 위의 식은 선형결합
    • u의 스칼라 계수와 nXm행렬의 선형결합

2.3 전치행렬

  • 행렬의 행들과 열들을 맞바꾼 것. __ $M^T$
  • 성질
    • $(A + B)^T=A^T+B^T$
    • $(cA)^T=cA^T$
    • $(AB)^T = B^TA^T$
    • $(A^T)^T = A$
    • $(A^{-1})^T=(A^T)^{-1}$

2.4 단위행렬 (identity matrix)

  • 정의 : 주대각 성분만 1 이고, 나머지는 0인 정방행렬__ I
    • $$\mathbf{I} = \begin{pmatrix}
      1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}
      $$
  • 정방행렬(square metrix) : 행과 열 수가 같은 정사각형 행렬
  • 주대각 성분(main diagonal) : 좌상->우하의 주된 대각선에 있는 성분
  • 성질
    • 어떤 행렬에 단위행렬을 곱해도 그 행렬 변화x
    • $AI = A$

2.5 행렬식 (determinant)

  • 정의 : 정방행렬을 입력받아서 실숫 값을 출력 ___ $det A$
  • 정방행렬 A는 만일 det A != 0, 일 때만 가역행렬이다

2.5.1 소행렬 (minor matrix)

  • m x n 행렬 A가 주어 졌을 떄, $\overline A_{ij}$ 는 i번쨰 행과 j번쨰 열을 삭제해서 나온 (n-1) x (m-1) 행렬

2.6 딸림행렬 (adjoint matrix)

  • 여인수 행렬
    • 여인수(corfactor) : $C_{ij} = (-1)^{i+j} det \overline A_{ij}$
    • 정의 : A의 각 성분을 여인수 계산해서 배치한 행렬 $C_A$
    • $$
      C_A = \begin{pmatrix}
      (-1)^{1+1} M_{11} & (-1)^{1+2} M_{12} & (-1)^{1+3} M_{13} \\(-1)^{2+1} M_{21} & (-1)^{2+2} M_{22} & (-1)^{2+3} M_{23} \\(-1)^{3+1} M_{31} & (-1)^{3+2} M_{32} & (-1)^{3+3} M_{33}
      \end{pmatrix}$$
  • 딸림 행렬
    • $C_A$의 전치행렬을 A의 딸림행렬 (adjoint matrix) 라고 함.
    • $A^{*}=C^{T}_{A}$

2.7 역행렬 (inverse matrix)

    • 정의 : 행렬 대수에서의 곱셈의 역원
      • 정방행렬에만 존재
      • n x n 행렬 $M$의 역은 n x n 행렬 $M^{-1}$
      • 역행렬이 있다 = 가역행렬(invertible matrix)
      • 역행렬이 없다 = 특이행렬(singular matrix)
      • 역행렬이 존재하는 경우, 그 역행렬은 고유
      • $MM^{-1} = M^{-1}M = I$ (행렬과 그 역행렬의 곱은 단위행렬)
    • 예시
      • $p^{'}=pM$ 행렬 방정식이 있을 떄, $p^{'}$와 $M$을 알고 $M$은 가역행렬이면 $p$는 다음과 같이 구할 수 있다.
      • $p^{'}M^{-1}=pMM^{-1}$ // 등식의 양변에 $M^{-1}$ 곱하기
      • $p^{'}M^{-1}=pI$ // 역행렬 정의에 의해 $MM^{-1}=I$
      • $p^{'}M^{-1}=p$ // 단위행렬 정의에 의해 $PI = p$
    • 예시 (딸림 행렬, 행렬식 이용)
      • $A = \begin{pmatrix}a_{11} & a_{12} \\ a_{21} & a_{22}\end{pmatrix}$ , $\det(A) = a_{11}a_{22} - a_{12}a_{21}$
      • $C_A = \begin{pmatrix}a_{22} & -a_{12} \\ -a_{21} & a_{11}\end{pmatrix}$, $C^T_A = \begin{pmatrix}a_{22} & -a_{21} \\ -a_{12} & a_{11}\end{pmatrix}$
      • $$A^{-1} = \frac{1}{\det A} C^T_A = \frac{1}{a_{11}a_{22} - a_{12}a_{21}} \begin{pmatrix}
        a_{22} & -a_{21} \\ a_{12} & a_{11}
        \end{pmatrix}
        $$
        • 행렬 M에 대해 위의 식을 적용하면,
        • $$
          M = \begin{pmatrix}
          3 & 0 \\ 1 & 2
          \end{pmatrix}
          $$
          $$ \det(M) = (3 \cdot 2) - (0 \cdot -1) = 6$$
          $$
          C_M = \begin{pmatrix}
          2 & 1 \\ 0 & 3
          \end{pmatrix}
          $$
          $$
          C^T_M = \begin{pmatrix}
          2 & 0 \\ 1 & 3
          \end{pmatrix}
          $$
          $$
          M^{-1} = \frac{1}{\det M} C^T_M = \frac{1}{6} \begin{pmatrix}
          2 & 0 \
          1 & 3
          \end{pmatrix} = \begin{pmatrix}
          \frac{1}{3} & 0 \\ \frac{1}{6} & \frac{1}{2}
          \end{pmatrix}
          $$
          $$
          M \cdot M^{-1} = \begin{pmatrix}
          3 & 0 \\ 1 & 2
          \end{pmatrix}
          \begin{pmatrix}
          \frac{1}{3} & 0 \\ \frac{1}{6} & \frac{1}{2}
          \end{pmatrix} = \begin{pmatrix}
          1 & 0 \\ 0 & 1
          \end{pmatrix} = \begin{pmatrix}
          \frac{1}{3} & \frac{1}{6} \\ 0 & \frac{1}{2}
          \end{pmatrix}
          \begin{pmatrix}
          3 & 0 \\ 1 & 2
          \end{pmatrix}
          $$
        • A와 B가 같은 차원의 가역 정방행렬 일 떄, 다음이 성립
          • $(AB)^{-1} = B^{-1}A^{-1}$
          • 위의 식을 증명하려면,
          • $(AB)(B^{-1}A^{-1}) = A(BB^{-1})A^{-1} = AIA^{-1} = AA^{-1} = I$
          • $(B^{-1}A^{-1})(AB) = B^{-1}(A^{-1}A)B = B^{-1}IB = B^{-1}B = I$

번외

      • 코사인 법칙
        • $c^2 = a^2 +b^2 - 2 ab cos(\theta)$

'프레임워크 > DirectX' 카테고리의 다른 글

Direct3D의 초기화_게임프레임워크&타이머 코드 의문점  (0) 2025.03.07
04.Direct3D의_초기화03  (1) 2025.03.04
04.Direct3D의_초기화02  (0) 2025.03.04
04.Direct3D의_초기화01  (1) 2025.03.01
01.Vector  (0) 2025.02.25

1.1 벡터

  • 정의 : 크기(magnitude)와 방향(direction)을 모두 가진 수량(quantity)
    • 공식적으로 maginitudedirection을 모두 가진 quantity벡터 값 수량(vector-valued quantity) 이라 부름.
    • ex ) 힘(force), 변위(displacement), 속도(velocity)
  • 표현 : 지향 선분(directed line segment)
    • 길이 = 크기
    • 화살표 = 방향
    • 위치는 중요 X <- 위치의 변화는 벡터의 크기, 방향을 변화 시키지 않음
  • 상등(equal) 조건 :
    • 벡터의 크기 & 방향이 같을 것
  • 문자 표현 (원점 좌표 기준)
    • v = (x, y, z)
      • 원점에서 시작할 경우, 끝 좌표를 알면 directionmagnitude를 알 수 있음

1.1.1 벡터 좌표계

  • 기준계에 따라 벡터의 좌표는 다름
    • 벡터의 방향과 크기 변화 x
  • 왼손잡이 좌표계 vs 오른손
  •  

1.1.2 기본 벡터 연산

  • *$u = (u\{x}, u_{y}, u_{z})$ *$v = (v\{x}, v_{y}, v_{z})$
    • $u_{x} = v_{x},; u_{y} = v_{y},; u_{z} = v_{z}$ 일 때에만 u = v
    • 벡터 덧셈은 성분 끼리 이루어짐. (같은 차원의 벡터 끼리만 가능)
    • 벡터에 스칼라(scalar, 크기만 있는 수량)을 곱하면, 결과는 벡터
      • **$ku = (ku_{x}, ku_{y}, ku_{z})$
    • 벡터 뺄셈은 벡터 덧셈과 스칼라 곱셈을 통해 정의
      • $v − u = v + (−1 · u) = v + (−u) = (v_{x} − u_x, v_y − u_y, v_z − u_z)$
      • 벡터의 뺄셈은 u 에서 v로 가는 벡터에 해당

1.2 길이와 단위 벡터

  • 벡터의 크기 표현(Scalar) : ||u||
    • $||u|| = \sqrt{y^2+x^2 + z^2}$
  • 단위 벡터(unit vector)
    • 크기가 1인 벡터
    • 정규화(normalization)를 통해 만듦
    • $\hat{v} = \frac{u}{||u||}$

1.3 내적 (inner product)

  • 결과가 스칼라로 나옴
    • $\mathbf{u} \cdot \mathbf{v} = u_x v_x + u_y v_y + u_z v_z$
  • $\mathbf{u} \cdot \mathbf{v} = ||\mathbf{u}||;||\mathbf{v}||\cos{\theta}$
    • 벡터의 내적이 벡터 사이의 각도의 코사인을 벡터 크기로 비례한 것임을 뜻 함
  • **(벡터(u) $\cdot$ 단위벡터($\hat{n}$))x$\hat{n}$ = u를 $\hat{n}$방향 투영 벡터
  • 속성
    • $\mathbf{u} \cdot \mathbf{v} = 0$ 이면 $\mathbf{u} \perp \mathbf{v}$ 직교
    • $\mathbf{u} \cdot \mathbf{v} > 0$ 이면, 사이각은 90도 보다 작음
    • $\mathbf{u} \cdot \mathbf{v} < 0$ 이면, 사이각은 90도 보다 큼
  • 1.3.1 직교화
  • 정규직교(orthonormal) : 모든 벡터가 단위 벡터이고, 서로 직교
    • 직교화(orthogonalization) : 정규직교벡터 집합으로 만드는 것
    • 그람-슈미트 직교화
      • 기본 단계 : $w_0 = v_0$ 설정
      • $1 ; <= ; i ; <= ; n-1$ 에 대해, $w_i = v_i - \sum_{j=0}^{i-1}proj_{w_j}(v_i)$
      • 정규화 단계 : $w_i = \frac{w_i}{||w_i||}$

1.4 외적 (outer product)

  • 두 벡터가 이루는 평면에 수직인 벡터를 생성하는 연산
    • 결과는 벡터
    • 외적의 계산은 일반적으로 오른손 좌표계를 기준으로 계산됨
      • 왼손 좌표계의 경우, 부호를 바꿔줄 필요가 있음
    • 왼손 엄지 법칙 : 엄지는 기준 벡터, 나머지 손가락은 남은 벡터, 손바닥은 생성되는 결과 벡터 방향
  • $\mathbf{w} = \mathbf{u} \times \mathbf{v} = \begin{vmatrix} \mathbf{i} & \mathbf{j} & \mathbf{k} \\ u_x & u_y & u_z \\ v_x & v_y & v_z \\ \end{vmatrix} = \left( u_y v_z - u_z v_y, u_z v_x - u_x v_z, u_x v_y - u_y v_x \right)$
  • 외적에는 교환법칙 성립x

번외

  • 병진 이동(translation) : 상하좌우로 평행 하게 이동하는 것.
  • 역벡터(inverse vector) : 벡터의 방향을 반대로 한 벡터
    • 더하기의 항등원 : 벡터 + 역베터 = 영벡터
      • $\mathbf{v} + (-\mathbf{v}) = \mathbf{0}$
    • 벡터의 거듭제곱 : 벡터와 역벡터의 크기는 동일, 방향은 반대
  • 영벡터(zero-vector) : 성분들이 모두 0인 벡터
  • 정규화(normalization) : 임의의 벡터를 단위 벡터로 만드는 것
  • 코사인 유사도(Cosine Similarity) : 내적공간의 두 벡터간 각도의 코사인값을 이용하여 측정된 벡터간의 유사한 정도

삼각함수 보각

  •  

'프레임워크 > DirectX' 카테고리의 다른 글

Direct3D의 초기화_게임프레임워크&타이머 코드 의문점  (0) 2025.03.07
04.Direct3D의_초기화03  (1) 2025.03.04
04.Direct3D의_초기화02  (0) 2025.03.04
04.Direct3D의_초기화01  (1) 2025.03.01
02.Matrix  (0) 2025.02.25

포인터

  • 변수 속성 4가지 (일반 변수와 동일)

      1) 이름 2) 타입 3) 값 4) 메모리 주소
  • 크기 :

    • 32bit 운영체제 : 4byte
    • 64bit 운영체제 : 8byte
  • 메모리 주소를 값으로 가지는 변수(데이터 타입)
    1) 변수의 접근이나 제어가 불가능한 영역에서 해당 변수에 접근해서 값을 제어할 수 있게 해줌.
    2) 동적할당(new)를 통해 런타임 떄 메모리 할당 변수를 제어할 수 있게 해줌.

  • 동적할당 (new 키워드, delete 키워드)
    1) 개념 : Heap메모리에 자료 저장 공간을 할당하는 것

  • 스택 메모리 : 특정 구문 내에서 선언된 변수들이 저장되는 메모리 공간

  • 힙 메모리 : 프로그래머가 자유롭게 할당하고 해제할 수 있는 메모리 공간

      1) 데이터의 크기가 일정하지 않은 형태의 자료형을 저장할 때 사용
      2) 서로 다른 구문 { ...a... } {...b...}에서 다른 구문의 변수의 값을 제어하고 싶을 때 사용
  • 주의사항

    • int* a, b 의 경우, a포인터, bint타입
    • delete {pointer}; {pointer} = nullptr 방식 사용 권장
      • 해제한 포인터는 nullptr로 변경해주는 것이 안전

더블 포인터 배열

int** arr = new int*[SIZE_X];
for (int i = 0; i < SIZE_Y; ++i)
{
    arr[i] = new int[SIZE_Y];
}
  • 일반적으로 포인터는 고정된 크기입니다.

    • 2D 배열을 구성할 떄, 연속된 고정값의 포인터 배열이 구성될 수 있습니다.
    • x64 기준 : (8bit * SIZE_Y) * SIZE_X
  • 지양해야 하는 이유

    • 포인터를 참조하기 때문에 구조가 복잡하다.
    • 메모리 관리를 하기 복잡해진다.
    • 컨테이너, 참조, 스마트 포인터가 생기면서 더 좋은 방식으로 대체되었다.

스마트 포인터

  • 메모리 관리를 자동화하여 메모리 누수를 방지하고, 코드의 안정성을 높임.

    • 자동으로 동적 메모리 해제
    • 포인터를 감싸 객체로 만듦
  • std::unique_ptr

    • 단독 소유권을 가지는 스마트 포인터 (같은 객체를 여러 포인터가 가리킬 수 없음)

    • 소유권 이전하려면, std::move() 사용

    • std::unique_ptr<int> ptr1 = std::make_unique<int>(42)

    • c++20 부터는 make_unique_for_overwrite() 사용하여 값 초기화를 발생 안 시키는 방법 가능

    • get() 내부 포인터에 직접 접근; 일반 포인터 전달 함수에 스마트 포인터 전달할 떄 용이

        ProcessData(uniquePtr.get());
    • reset() : 리소스 해제 후, nullptr로 초기화

    • release() : 소유권 해제; 해제 포인터 반환(해제된 포인터는 직접 관리 필요)

    • 일반 적인 함수에 전달 사용 예시 (내부에서 해제 못하게 const 참조 전달 권장)

        #include <iostream>
        #include <memory>
      
        void useUniquePtr(const std::unique_ptr<int>& ptr) {
            std::cout << "Value: " << *ptr << std::endl;
        }
      
        int main() {
            auto ptr = std::make_unique<int>(42);
            useUniquePtr(ptr); // 소유권 전달 X, 값 사용만
            std::cout << "Still owned: " << *ptr << std::endl;
      
            return 0;
        }
    • 함수에 소유권 전달 사용 예시 (내부에서 해제)

        #include <iostream>
        #include <memory>
      
        void takeUniquePtr(std::unique_ptr<int> ptr) {
            std::cout << "Taken Value: " << *ptr << std::endl;
            // ptr이 함수가 끝나면 자동으로 해제됨
        }
      
        int main() {
            auto ptr = std::make_unique<int>(42);
            takeUniquePtr(std::move(ptr)); // 소유권 이동
            // std::cout << *ptr << std::endl; // 이 줄은 실행 시 오류 발생 (ptr은 소유권을 잃음)
      
            return 0;
        }
    • 함수의 값으로 반환 시, 소유권이 이전 됨

      • 참조로 받고 값으로 반환 시, 원래 참조를 통해 관리되던 객체가 더 이상 소유되지 않게 됨
    • 헤더 파일에서 전방 선언으로 사용 시, 사용자 정의 생성자&소멸자 필요

      • unique_ptr은 완전한 타입의 소멸자를 인라인으로 포함시키기 때문
      • 생성자와 소멸자가 구현파일에서 정의됨을 명시해야 전방선언한 클래스의 완전한 타입을 알 수 있다고 판단함
  • std::shared_ptr

    • 여러 개의 포인터가 같은 객체를 공유할 수 있도록 지원

    • 내부적으로 참조 카운트를 유지, 카운트가 0 이 되면 메모리 해제

      • std::shared_ptr<int> ptr1 = std::make_shared<int>(100);
    • 동일 객체를 가르킬 떄, 원본 객체를 가르키는게 아니라 관리하고 있는 shared_ptr를 참조해야 카운트가 늘어난다.

        std::shared_ptr<int> ptr1 = std::make_shared<int>(100);
        std::shared_ptr<int> ptr2 = ptr1;
    • 값으로 전달 사용하는 경우, 참조 카운트가 늘어남; 함수 종료 시, 카운트 다시 감소

        #include <iostream>
        #include <memory>
      
        void processSharedPtr(std::shared_ptr<int> ptr) {
            std::cout << "Shared value: " << *ptr << std::endl;
            std::cout << "Reference count inside function: " << ptr.use_count() << std::endl;
        }
      
        int main() {
            auto shared = std::make_shared<int>(42);
            std::cout << "Reference count before function: " << shared.use_count() << std::endl; // 1
      
            processSharedPtr(shared); // 값을 전달  // 2
            std::cout << "Reference count after function: " << shared.use_count() << std::endl; // 1
      
            return 0;
        }
    • 읽기 전용으로만 사용 시에는 참조로 넘기는 걸 권장 (참조 카운트 증가 X)

        #include <iostream>
        #include <memory>
      
        void readSharedPtr(const std::shared_ptr<int>& ptr) {
            std::cout << "Read value: " << *ptr << std::endl;
            std::cout << "Reference count: " << ptr.use_count() << std::endl;
        }
      
        int main() {
            auto shared = std::make_shared<int>(42);  // 1
            readSharedPtr(shared); // 소유권 변경 없이 사용  // 1
            std::cout << "Reference count after function: " << shared.use_count() << std::endl; // 1
      
            return 0;
        }
    • 함수의 값으로 반환 시, 공유 소유권이 확장 됨

      • 참조로 받고 값으로 반환 시, 해당 객체에 대한 참조 카운트가 증가
  • std::weak_ptr

    • std::shared_ptr와 함께 사용, <- 참조 카운트를 증가 시키지 않음

    • std::shared_ptr가 존재하는지 확인하는 데 사용

      #include <iostream>
      #include <memory>
      
      int main() {
        std::shared_ptr<int> ptr1 = std::make_shared<int>(100);
        std::weak_ptr<int> weakPtr = ptr1;
      
        if (auto sharedPtr = weakPtr.lock()) {
            std::cout << "sharedPtr 가리키는 값: " << *sharedPtr << std::endl;
        } else {
            std::cout << "sharedPtr는 nullptr입니다." << std::endl;
        }
      
        return 0;
      }
      

NULL & NullPtr

  • c++ 에서는 NULL 은 숫자 0 으로 정의.
  • c++11 버전에서 추가된 Nullptr은 int* 타입.
    • 불 표현식에서는 false로 취급

포인터가 가리키는 값이 상수인 경우 (const T*)

  • 포인터가 가리키는 값을 변경할 수 없음

  • 다른 주소를 가리킬 수 있음

      const int* ptr;
      int value1 = 10;
      int value2 = 20;
    
      ptr = &value1;  // 가능
      *ptr = 30;      // 컴파일 오류, ptr이 가리키는 값을 변경할 수 없음
      ptr = &value2;  // 가능
    

포인터 자체가 상수인 경우 (T* const)

  • 포인터가 가리키는 값 변경 가능

  • 다른 주소를 가리킬 수 없음

      int value1 = 10;
      int value2 = 20;
      int* const ptr = &value1;
    
      *ptr = 30;      // 가능
      ptr = &value2;  // 컴파일 오류, ptr이 다른 주소를 가리킬 수 없음
    

포인터와 가리키는 값 모두 상수인 경우 (const T* const)

  • 포인터가 가리키는 값을 변경할 수 없음

  • 다른 주소를 가리킬 수 없음

      const int value1 = 10;
      const int value2 = 20;
      const int* const ptr = &value1;
    
      *ptr = 30;      // 컴파일 오류, ptr이 가리키는 값을 변경할 수 없음
      ptr = &value2;  // 컴파일 오류, ptr이 다른 주소를 가리킬 수 없음
    

'공부 > C++' 카테고리의 다른 글

C++ 저장&링킹  (1) 2025.03.01
C++ 컨테이너 클래스  (0) 2025.03.01
new&delete  (0) 2025.03.01
Operator 기본 연산자  (0) 2025.02.21
C++ main()에 대해  (0) 2025.02.19

+ Recent posts

let textNodes = document.querySelectorAll("div.tt_article_useless_p_margin.contents_style > *:not(figure):not(pre)"); textNodes.forEach(function(a) { a.innerHTML = a.innerHTML.replace(/`(.*?)`/g, '$1'); });