https://school.programmers.co.kr/learn/courses/30/lessons/42860

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

그리디하게 접근하는 것이 중요하다고 생각했다.

1.알파벳 기준 절반이상이면 왼쪽으로 이동하는것이 이득, 아니라면 오른쪽으로 이동하는것이 이득

2.커서도 동일하게 길이의 절반이상이면 왼쪽으로 이동하는 것이 이득, 아니라면 오른쪽으로 이동

이렇게 생각하고 min으로 A부터 움직였을 때와 Z부터 움직였을 때의 최소값을 구한다.

그리고 커서를 움직이는 것을 생각해보면 

왼쪽으로 움직일때와 오른족으로 움직일때의 최솟값을 계산하면 된다. 

 

 

#include <string>
#include <algorithm>
using namespace std;
//알파벳 기준 절반이상이면 왼쪽으로 이동하는것이 이득, 아니라면 오른쪽으로 이동하는것이 이득
//커서도 동일하게 길이의 절반이상이면 왼쪽으로 이동하는 것이 이득, 아니라면 오른쪽으로 이동
int solution(string name) {
    int n = name.length();
    int answer = 0;
    
    // 각 알파벳을 바꾸는 데 필요한 조작 횟수를 합산
    for (int i = 0; i < n; ++i) {
        answer += min(name[i] - 'A', 'Z' - name[i] + 1);
    }
    
    // 커서 이동의 최소 조작 횟수를 구함
    int move = n - 1;
    for (int i = 0; i < n; ++i) {
        int next = i + 1;
        while (next < n && name[next] == 'A') {         //A건너뛰기 
            ++next;
        }
        move = min(move, i + n - next + min(i, n - next));      //왼쪽으로 갈때, 오른쪽으로 갈때 비교
    }
    
    return answer + move;
}

 

이제 본격적으로  C++를 통해 언리얼을 다뤄보자 

 

일단 툴/C++클래스추가 에서 액터추가에서 C++ 클래스를 만들어주자 

 

 

기본 코드를 살펴보자면 

Item.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"

UCLASS()
class SLASH_API AItem : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AItem();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

};

 

리플랙션 코드와 액터의 기능을 쓸 수 있게하는 헤더 파일들과 AActor를 상속받고 있는 모습을 볼 수 있다. 

Item.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "Items/Item.h"

// Sets default values
AItem::AItem()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;		//매틱마다 실행되도록

}

// Called when the game starts or when spawned
void AItem::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AItem::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

 

Actor 쪽에서 무언가 구현할 것을 대비하여 부모쪽의 BeginPlay를 실행하기위해  Super::로 실행한다. 

 

이제 만든 클래스를 바탕으로 블루프린트를 만들어보자 

모든 액터는 초기에 DefaultSceneRoot라는 Component를 가지고 있다.

 

모든 블루프린트에는 이벤트 그래프가 있는데 이것은 블루프린트를 통해 코드의 흐름을 작성하고 실행시킬 수 있는 도구이다. 

예를 들면 Begin Play 이벤트 즉 시작할 때 String을 화면에 띄우는 기능을 블루프린트를 통해 구현할 수 있는 것이다.

그리고 다음은 Construction Script이다

이것은 게임이 시작되기 전에 실행되며 블루프린트의 속성이 바뀔 때 실행된다.

 

그리고 만약 이 함수가 실행되는 곳, 즉 액터와 너무 멀리있다면 실행결과가 화면에 보이지 않는다 

 

 

 

이제 이것을 C++를 통해 구현해보자

// Called when the game starts or when spawned
void AItem::BeginPlay()
{
	Super::BeginPlay();
	
	UE_LOG(LogTemp,Warning,TEXT("Begin Play called!"));
}

 

 

중요한점 

 

핫리로딩 - 라이브컴파일을 하려면  밑의 버튼을 누르면 된다.

비쥬얼 스튜디오에서 ctrl shift b 를 누르면 컴파일이 된다.

 

 

 

이 문제에서 중요한 점은

블록에 적힌 번호가 n일때 가장 첫 블록은 n*2번째 위치에 설치하고 그 다음은 n*3, n*4에 설치한다.

기존에 설치된 블록을 빼고 새로운 블록을 집어 넣을 수 있다

만약 1부터 시작이라면 입출력 예시와 같이 제일 앞에 0이 있어야한다. 

이때 12번 블록에 들어갈 블록을 생각해보면 

12번 블록의 약수는 1,2,3,4,6,12이다 이때 최종적으로 들어가게 되는 블록은 제일 큰 약수인 6이다. 

만약 6번째 블록은 12번째 블록에 설치되는 것이다. 

이 약수는 제일 작은 약수에서 원래 값을 나누어주면 된다.

그리고 이 약수가 10,000,000보다 낮은 경우에만 블록으로 사용 가능하기 때문에 만약 가장 큰 약수가 이 보다 크다면 앞에서부터 숫자를 늘려가면서 원래값에 나눠주면 그보다 작은 값을 얻을 수 있다.

#include <string>
#include <vector>
#include <cmath>
using namespace std;
const int MAX_VALUE=10000000;

long long FindMaxDiv(int num)
{
    long long result=1;
    
    for(int i=2;i<=sqrt(num);i++){
        if(num%i==0){
            result=i;
            
            if(num/i<=MAX_VALUE){
                result=num/i;
                break;
            }
        }
    }
    
    return result;
}

vector<int> solution(long long begin, long long end) {
    vector<int> answer;
    for(int i=begin;i<=end;i++){
        if(i==1){
           answer.push_back(0);
           continue;
        }
        answer.push_back(FindMaxDiv(i));
    }
    return answer;
}

 

https://school.programmers.co.kr/learn/courses/30/lessons/181188

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

그리디하게 접근하는 것이 중요하다. 

핵심은 end포인트를 기준으로 오름차순 정렬하고 설정된 end포인트보다 Start 포인트가 작은 미사일은 요격해주고 아닌 경우에는 요격포인트를 새로 설정해주는 것이다. 

#include <string>
#include <vector>
#include <algorithm>
using namespace std;
//그리디하게 접근
bool cmp(vector<int> &a,vector<int> &b){
    if(a[1]==b[1]) return a[0]<b[0];
    else return a[1]<b[1];
}
int solution(vector<vector<int>> targets) {
    int answer = 0;
    sort(targets.begin(),targets.end(),cmp);
    int end=targets[0][1];
    answer++;
    for(int i=1;i<targets.size();i++){
        //만약 시작값이 설정해둔 끝점보다 작다면 요격가능
        if(targets[i][0]<end) continue;
        //아니라면 새로운 요격포인트 지정
        else{
            answer++;
            end=targets[i][1];
        }
    }
    return answer;
}

 

https://www.udemy.com/course/unreal-engine-5-the-ultimate-game-developer-course/

 

 

언리얼 엔진에서 Large World Open World

 

Large World는 하나의 맵에서 다른 맵으로 이동하면 기존의 맵을 언로드하고 다른 맵을 로드하는 방식으로 작동한다.

 

Open World는 하나의 큰맵에서 섹션을 나누고 플레이어가 있는 섹션만 로드한다. -> 월드 파티션 

언리얼에서 제공해주는 레벨 생성에에는오픈월드가 있다.

오픈월드 상의 미니맵

 

맵의 구성요소를 살펴보자 

1. 하늘 

하늘을 구성하는 요소에는

●Sky AtmosPhere(대기) 

실제 지구의 대기처럼 빛을 산란시켜 준다. 두개의 다른 광원을 통해 달과 태양을 구현할 수도 아니면 2개의 태양을 구현할 수 도 있다.

● Directional Light(지향성 광원)

한방향으로 나아가는 빛으로 보통 태양빛으로 사용된다. 

대기와 지향성광원 추가화면
광원 2개

이때 라이트의 기동성은 

static 일때는 위치나 방향,강도 색깔도 바꿀 수 없지마 연산이 빠르다

Stationary - 색, 강도 변경가능하지만 위치, 회전 변경불가 정적인 물체에 빛을 비춰 그림자생성

Movable - 동적 그림자생성, 가장 비싸지만  현실감\

 

●Sky Light - 먼거리의 부분을 포착, 게임전체에 균일한 조명 실시간 캡처가능

안개와 구름 

● Exponential height Fog

안개 효과는 높이에 따라 안개의 밀도가 기하급수적으로 증가하는 특성을 가지고 있다.

● Volumetric Clouds

동적 구름 

Fog와 Clouds 적용화면

그 다음 온도와 회전을 달리주면 이렇게 된다. 

결과화면

 

풍경- 땅 만들기

LandScape는 여러 매쉬가 펼처저 있는 것으로 이것을 수정하고 조작하는 것으로 여러 땅모양을 만들 수 있다.

랜드스케이프 모드로 들어간 후 32 x 32 짜리의 Landscape를 만들어주자

LandScape 만든 결과

 

조각과 침식등을 추가해 사막처럼 만들어보았다.

 

이후Material을 추가해주었다. 

Material은 새로운 Material을 만든 뒤 여러 Material을 조합해서 만들어주었다.

만든 Material을 LandScape 머티리얼로 넣어주고 페인트의 레이어에서 +한뒤 Weight 로 선택해서 만들어주면 된다.

 

결과화면

https://school.programmers.co.kr/learn/courses/30/lessons/160585

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

틱텍토 판이 주어질 때 이게 규칙을 지켜서 진행한 틱택토에서 나올 수 있는 상황인지 검사하면 되는 것이다

O가 선공 X가 후공이다. 

안되는 조건을 모두 검사하는 것이 중요하다. 

O의 개수가 X보다 많거나 같아야하고 많을 때는 2개이상 많아질 수 없다. 

둘 다 이길 수는 없다.

만약 O가 이겼을 때는 X의 개수보다 1개 많아야한다.

만약 X가 이겼을 때는 X가 후공이여서 O의 개수와 같은 수여야한다.

#include <string>
#include <vector>

using namespace std;
//O와 X의 개수를 세고 안되는 조건인지 검사
bool isWin(const vector<string>& board,char mark)
{
    //가로
    for(int i=0;i<3;i++)
    {
        if(board[i][0]==mark && board[i][1]==mark && board[i][2]==mark) return true;
    }
    //세로
    for(int i=0;i<3;i++)
    {
        if(board[0][i]==mark && board[1][i]==mark && board[2][i]==mark) return true;
    }
    if(board[0][0]==mark && board[1][1]==mark && board[2][2]==mark) return true;
    if(board[0][2]==mark && board[1][1]==mark && board[2][0]==mark) return true;
    
    return false;
}

int solution(vector<string> board) {
    int ocnt=0,xcnt=0;

    
     // O와 X의 개수를 센다.
    for (const string& row : board) {
        for (char cell : row) {
            if (cell == 'O') ocnt++;
            else if (cell == 'X') xcnt++;
        }
    }
    
    if(xcnt>ocnt) return 0;
    
    if(ocnt>xcnt+1) return 0;
    
    bool winO =isWin(board,'O');
    bool winX =isWin(board,'X');
    
    //둘 다 이길수는 없다
    if(winO && winX) return 0;
    //만약 O가 이겼을 때 O의 개수는 X보다 1개많아야 한다.
    if(winO && ocnt!=xcnt+1) return 0;
    //만약 x가 이겼을 때 x의 개수는 O와 같아야한다. 
    if(winX && xcnt!=ocnt) return 0;
    
    return 1;
}

 

 

https://school.programmers.co.kr/learn/courses/30/lessons/12952

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

N-Queen 문제는 대표적인 백트래킹 문제이다. 

이때 백트래킹은 

모든 가능한 경우를 탐색하기 위해 사용하는 기법 중 하나로, 탐색 과정에서 조건을 만족하지 않는 경우를 미리 배제함으로써 탐색 효율을 높인다. 

 

이문제를 백트래킹으로 푸는 과정은 다음과 같다.

 

  • 체스판의 각 열에 대해 퀸을 한 행씩 배치한다.
  • 퀸을 배치할 때마다 현재 퀸이 다른 퀸과 공격하지 않는지 확인한다.
  • 만약 조건을 만족하지 않는다면 해당 배치를 포기하고, 다른 행에 퀸을 배치해 본다. - 백트래킹
  • 모든 퀸을 배치할 수 있다면 경우의 수를 추가해준다.
  • 모든 가능한 배치를 탐색한 후 경우의 수를 반환 

abs(positions[i] - col) == abs(i - row) -> 1이나 -1 방향에 퀸이 있는지 확인 -> 대각선

#include <string>
#include <vector>
#include <cmath>

using namespace std;

bool isValid(const vector<int>& positions, int row, int col) {
    for (int i = 0; i < row; ++i) {
        // 같은 열에 퀸이 있는지, 또는 대각선에 퀸이 있는지 확인
        if (positions[i] == col || abs(positions[i] - col) == abs(i - row)) {
            return false;
        }
    }
    return true;
}

void solveNQueens(int n, int row, vector<int>& positions, int& answer) {
    if (row == n) {
        // 모든 퀸을 배치한 경우
        ++answer;
        return;
    }

    for (int col = 0; col < n; ++col) {
        if (isValid(positions, row, col)) {
            positions[row] = col;
            solveNQueens(n, row + 1, positions, answer);
            positions[row] = -1; // 백트래킹
        }
    }
}

int solution(int n) {
    int answer = 0;
    vector<int> positions(n, -1); // 각 열에 대한 퀸의 위치
    solveNQueens(n, 0, positions, answer);
    return answer;
}

 

https://www.youtube.com/watch?v=ljmOsZVrtok&list=PLiSlOaRBfgkcPAhYpGps16PT_9f28amXi&index=22

오늘은 AI가 플레이어를 감지하고 쫓아 다닐 수 있도록 만들어보자 

 

일단 AI_Controller에 컴포넌트를 추가해서 플레이어를 감지할 수 있도록 AIPerception 컴포넌트를 추가해준다.

이 컴포넌트에서 감지환경설정을 시야구성으로 바꿔준다.

이때 센스는 기본을 따르도록 하고 추후과정에서 이 값을 조절하기로 하자.

AIPerception 컴포넌트

귀속감지는 블루프린트 상의 오류방지를 위해 모두 체크해주자

 

타킷 퍼셉션 업데이트 시 이벤트 구성

 

블랙보드에 키를 추가해서 만약 감지했을 때 Target Actor이면 따라가고 아니면 돌아다니는 과정을 수행하도록 한다.

 

이제 AI 블랙보드 트리에서 Chase Target Sequence를 만들어주고 새로운 테스크를 만들어주자 

 

블랙보드 키를 가져와서 액터에 넣어주고 이를 따라가게 하자 멈추는 거리는 100정도로 설정해주자 

 

이후 블랙보드트리에서 

왼쪽에 Chase를 붙여서 이게 먼저 실행될 수 있도록 하며 BlackBoard값에 따라 해당 테스크가 수행되도록 데코레이터를 추가해준다.

전체 트리 및 Chase쪽 데코레이터 설정
Patrol쪽 데코레이터 설정

 

하지만 아직 플레이어를 감지하지 못하고 있다. 이 부분은 플레이어 쪽에 감지 자극소스를 부착해주면된다.

 

그리고 프로젝트 루트폴더에서 Config/DefaultGame.ini 파일을 수정해준다.

https://school.programmers.co.kr/learn/courses/30/lessons/42890

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

조합을 통해 키를 조합한 후 이에 대한 유일성과 최소성을 만족시키는 값을 찾아야한다.

 

유일성 : 유일한 값 

최소성 : 여러 속성을 조합해 만든 키가 유일성을 가질 때, 한 속성을 제외해도 유일성이 유지되면 최소성을 만족하지 않는다.

 

어떻게 검사할까 찾아보다가 비트마스크 방법으로 조합과 유일성 최소성 검사를 하는게 직관적이라는 것을 알게 되었다. 

조합

각 열을 하나의 비트로 나타내면, 비트마스크의 각 비트는 열의 포함 여부를 나타낼 수 있다. 

예를 들어, 열이 3개인 경우:

  • 000: 아무 열도 포함하지 않음
  • 001: 첫 번째 열만 포함
  • 010: 두 번째 열만 포함
  • 011: 첫 번째와 두 번째 열 포함
  • 100: 세 번째 열만 포함
  • 101: 첫 번째와 세 번째 열 포함
  • 110: 두 번째와 세 번째 열 포함
  • 111: 모든 열 포함

이런식으로 표현할 수 있다. 

이 방법을 통해 조합을 생성하려면 2^열의 수로 순회하면 된다. 

조합

 // 모든 가능한 속성 조합을 생성
    for (int comb = 1; comb < (1 << columns); comb++) {
        // 유일성과 최소성 검사
        if (isUnique(relation, columns, comb) && isMinimal(comb, candidateKeys)) {
            // 유일성과 최소성을 만족하는 경우 후보키로 추가
            candidateKeys.push_back(comb);
        }
    }

 

유일성 검사

유일성 검사또한 비트마스크를 통해 열 포함여부를 검사해준다. 

// 유일성을 검사하는 함수
bool isUnique(vector<vector<string>>& relation, int columns, int comb) {
    set<string> uniqueCheck;
    for (int i = 0; i < relation.size(); i++) {
        string key = "";
        for (int j = 0; j < columns; j++) {
            // comb의 각 비트를 확인하여 해당 열을 포함하는지 검사
            if (comb & (1 << j)) {
                key += relation[i][j] + " ";
            }
        }
        // 유일성 검사: 이미 존재하는 키라면 false 반환
        if (uniqueCheck.find(key) != uniqueCheck.end()) {
            return false;
        }
        uniqueCheck.insert(key);
    }
    return true;
}

 

이때 직접적인 검사는  if (comb & (1 << j)) 부분에서 이루어진다. 

comb & (1 << j) 이 부분에서 어떻게 계산이 이루어지는지는 예시를 통해 알아보자

 

  • comb의 j번째 비트가 1인지 검사하려면, 1 << j를 사용하여 j번째 비트만 1인 숫자를 만든다.
  • comb & (1 << j)를 수행하면, comb의 j번째 비트가 1일 때만 결과가 1이 됩니다. 그렇지 않으면 결과가 0이 된다.
  • comb = 5 (0101)
  • 열의 개수 columns = 4
  • j = 0:
    • 1 << 0는 0001
    • comb & 0001은 0101 & 0001이므로 결과는 0001 (참)
  • j= 1:
    • 1 << 1은 0010
    • comb & 0010은 0101 & 0010이므로 결과는 0000 (거짓)

 

  • j = 2:
    • 1 << 2은 0100
    • comb & 0100은 0101 & 0100이므로 결과는 0100 (참)
  • j = 3:
    • 1 << 3은 1000
    • comb & 1000은 0101 & 1000이므로 결과는 0000 (거짓)

j = 0과 j = 2에서 조건이 참이므로, 첫 번째와 세 번째 열의 값이 key에 추가

 

최소성 검사 

기존 후보키로 만들어진 것이 현재 조합의 부분집합에 포함된다면 후보키가 아니다.

// 최소성을 검사하는 함수
bool isMinimal(int comb, vector<int>& candidateKeys) {
    // 기존의 후보키들에 대해 검사
    for (int key : candidateKeys) {
        // 기존의 후보키가 현재 조합에 포함되는지 검사
        if ((comb & key) == key) {
            // 기존 후보키가 현재 조합의 부분집합이면 최소성 만족하지 않음
            return false;
        }
    }

    // 모든 기존 후보키가 현재 조합의 부분집합이 아니면 최소성 만족
    return true;
}

 

전체코드 

 

#include <iostream>
#include <vector>
#include <string>
#include <set>

using namespace std;

// 유일성을 검사하는 함수
bool isUnique(vector<vector<string>>& relation, int columns, int comb) {
    set<string> uniqueCheck;

    // 각 튜플(행)에 대해 검사
    for (int i = 0; i < relation.size(); i++) {
        string key = "";

        // comb의 각 비트를 확인하여 해당 열을 포함하는지 검사
        for (int j = 0; j < columns; j++) {
            if (comb & (1 << j)) {
                // comb의 j번째 비트가 1이면 해당 열을 key에 추가
                key += relation[i][j] + " ";
            }
        }

        // 유일성 검사: 이미 존재하는 키라면 false 반환
        if (uniqueCheck.find(key) != uniqueCheck.end()) {
            return false;
        }

        // 새로운 키를 set에 추가
        uniqueCheck.insert(key);
    }

    // 모든 튜플에 대해 유일성을 만족하면 true 반환
    return true;
}

// 최소성을 검사하는 함수
bool isMinimal(int comb, vector<int>& candidateKeys) {
    // 기존의 후보키들에 대해 검사
    for (int key : candidateKeys) {
        // 기존의 후보키가 현재 조합에 포함되는지 검사
        if ((comb & key) == key) {
            // 기존 후보키가 현재 조합의 부분집합이면 최소성 만족하지 않음
            return false;
        }
    }

    // 모든 기존 후보키가 현재 조합의 부분집합이 아니면 최소성 만족
    return true;
}

int solution(vector<vector<string>> relation) {
    int rows = relation.size();     // 튜플(행)의 개수
    int columns = relation[0].size(); // 속성(열)의 개수
    vector<int> candidateKeys;     // 후보키를 저장할 벡터

    // 모든 가능한 속성 조합을 생성
    for (int comb = 1; comb < (1 << columns); comb++) {
        // 유일성과 최소성 검사
        if (isUnique(relation, columns, comb) && isMinimal(comb, candidateKeys)) {
            // 유일성과 최소성을 만족하는 경우 후보키로 추가
            candidateKeys.push_back(comb);
        }
    }

    // 후보키의 개수 반환
    return candidateKeys.size();
}

int main() {
    vector<vector<string>> relation = {
        {"100", "ryan", "music", "2"},
        {"200", "apeach", "math", "2"},
        {"300", "tube", "computer", "3"},
        {"400", "con", "computer", "4"},
        {"500", "muzi", "music", "3"},
        {"600", "apeach", "music", "2"}
    };

    cout << solution(relation) << endl;  // 출력: 2
    return 0;
}

 

 

 

 

인덱스버퍼

현재 -> 사각형- 삼각형 2개  만약 이런식이라면 그 많은 도형을 표현하기엔 메모리가 너무 많이 소모된다.

-> 인덱스 버퍼사용

정점들에 인덱스(넘버)를 부여하여 사용

 

이걸 코드로 만들어보자 일단 정점을 하나 추가해보자 그리고 정점 순서에 맞게 좌표를 수정해준다.

//13 -> 012
//02 -> 213
_vertices[0].position = Vec3(-0.5f, -0.5f, 0.f);
_vertices[0].color = Color(1.f, 0.f, 0.f, 1.f);
_vertices[1].position = Vec3(-0.5f, 0.5f, 0.f);
_vertices[1].color = Color(1.f, 0.f, 0.f, 1.f);
_vertices[2].position = Vec3(0.5f, -0.5f, 0.f);
_vertices[2].color = Color(1.f, 0.f, 0.f, 1.f);
_vertices[3].position = Vec3(0.5f, 0.5f, 0.f);
_vertices[3].color = Color(1.f, 0.f, 0.f, 1.f);

 

인덱스버퍼에 필요한 변수를 만들고 Geometry에 인덱스 버퍼 부분을 만들어주자

변수 

//인덱스버퍼 - 이거도 Geometry에 포함
vector<uint32> _indices;
ComPtr<ID3D11Buffer> _indexBuffer = nullptr;
//Index
{
	_indices = { 0,1,2,2,1,3 };
}

//IndexBuffer
{
	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(desc));
	desc.Usage = D3D11_USAGE_IMMUTABLE;			//gpu만 read only
	desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	desc.ByteWidth = (uint32)(sizeof(uint32) * _indices.size());

	D3D11_SUBRESOURCE_DATA data;
	ZeroMemory(&data, sizeof(data));
	data.pSysMem = _indices.data();			//첫번째 시작주소 cpu값이 복사된다.

	HRESULT hr= _device->CreateBuffer(&desc, &data, _indexBuffer.GetAddressOf());
	CHECK(hr);
}

 

인덱스 버퍼 세팅

IA 세팅 과정 중에 -> 랜더에 있는 세팅부분에 추가해줘야한다. 

기존

	//IA - 세팅부분
	_deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer.GetAddressOf(),&stride, &offset);
	_deviceContext->IASetInputLayout(_inputLayout.Get());
	_deviceContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);		//삼각형으로 만들어주기

 

수정

	//IA - 세팅부분
	_deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer.GetAddressOf(),&stride, &offset);
	_deviceContext->IASetIndexBuffer(_indexBuffer.Get(), DXGI_FORMAT_R32_UINT,0);
	_deviceContext->IASetInputLayout(_inputLayout.Get());
	_deviceContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);		//삼각형으로 만들어주기
    
    
    //인덱스를 통해 그려주기
    _deviceContext->DrawIndexed(_indices.size(), 0, 0);

 

이렇게하면 Index버퍼를 사용할 준비가 완료되었다. 

 

실행화면

 

정점버퍼와 인덱스버퍼의 차이점

-> 인덱스버퍼는 삼각형에 넘어가는 정점 개수를 줄여줘 메모리적으로 효율적으로 작업할 수 있다.

 

정점버퍼와 인덱스버퍼의 조합으로 삼각형이 어떻게 만들어지는지 판단할 수 있다.

 

이제 사각에 이미지를 띄워보자 

 

중요한 점 : uv좌표, 쉐이더코드 수정

 

uv좌표 - 이미지의 좌상단 0,0 우하단 1,1로 했을 때 퍼센티지로 가져오기 위함 

uv좌표

 

Stuct.h 수정 및 정점정보 수정

#pragma once
#include "Types.h"


struct Vertex
{
	Vec3 position;		//12바이트 0부터시작
	//Color color;		//12부터시작
	Vec2 uv;			
};

 

//정점정보
{
	_vertices.resize(4);

	//13 -> 012
	//02 -> 213
	_vertices[0].position = Vec3(-0.5f, -0.5f, 0.f);
	_vertices[0].uv = Vec2(0.f, 1.f);
	//_vertices[0].color = Color(1.f, 0.f, 0.f, 1.f);
	_vertices[1].position = Vec3(-0.5f, 0.5f, 0.f);
	_vertices[1].uv = Vec2(0.f, 0.f);
	//_vertices[1].color = Color(1.f, 0.f, 0.f, 1.f);
	_vertices[2].position = Vec3(0.5f, -0.5f, 0.f);
	_vertices[2].uv = Vec2(1.f, 1.f);
	//_vertices[2].color = Color(1.f, 0.f, 0.f, 1.f);
	_vertices[3].position = Vec3(0.5f, 0.5f, 0.f);
	_vertices[3].uv = Vec2(1.f, 0.f);
	//_vertices[3].color = Color(1.f, 0.f, 0.f, 1.f);
}

 

그리고 이에 맞게 InputLayout을 고쳐줘야하는데 이 때 쉐이더와 맞춰주어야하기 때문에 

많이 사용하는 TEXCOORD - 텍스처의 좌를 사용한다. 

InputLayout 및 쉐이더 수정

struct VS_INPUT
{
    float4 position : POSITION;
    //float4 color : COLOR;
    float2 uv : TEXCOORD;
};

struct VS_OUTPUT
{
    float4 position : SV_POSITION;      //시스템 VALUE  무조건 있어야한다.
    //float4 color : COLOR;
    float2 uv : TEXCOORD;
};
	//입력에 대한 정보 ~바이트부터 뛰면 칼러인지 
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
		{"TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0}
	};

 

이미지파일 소스를 gpu쪽에도 건네줘서 알고있도록 해야한다. 

레지스터에 등록해주기

//함수의 인자같은 존재, 오브젝트마다 달라질수있다.
Texture2D texture0 : register(t0);      //레지스터에 등록
SamplerState sampler0 : register(s0);

 

등록해준 레지스터를 통해 텍스처 색상을 가져오기 

//모든 픽셀단위 대상 - 색상관련
float4 PS(VS_OUTPUT input) : SV_Target  //랜더 타켓하는 곳으로 보내주기
{
    float4 color = texture0.Sample(sampler0, input.uv);     //텍스처0번 uv좌표를 통해 색상 빼오기
    
    return color;
}

해당 이미지파일을 cpu에서 리소스로 가지고 gpu쪽에도 만들어준 다음 파이프라인에 연결

 

픽셀쉐이더부분에서 CreateSRV를 통해 할 수 있다.

SRV 쉐이더 리소스 뷰 만들어주기 

SRV변수 추가 -이미지를 어떻게 쓸것인가 

이것을 통해 이미지를 건네준다.  

//SRV - 이미지를 어떻게 쓸것인가
ComPtr<ID3D11ShaderResourceView> _shaderResourceView = nullptr;
void Game::CreateSRV()
{
	DirectX::TexMetadata md;
	DirectX::ScratchImage img;
	HRESULT hr = ::LoadFromWICFile(L"cat.png", WIC_FLAGS_NONE, &md, img);
	CHECK(hr);

	//쉐이더리소스뷰 만들기
	hr = ::CreateShaderResourceView(_device.Get(), img.GetImages(), img.GetImageCount(), md, _shaderResourceView.GetAddressOf());
	CHECK(hr);
}

 

이제 Init부분에 함수를 넣고 Render의 PS부분에서 이미지와 연결시켜주자 

void Game::Init(HWND hwnd)
{
	_hwnd = hwnd;
	_width = GWinSizeX;
	_height = GwinSizeY;


	CreateDeviceAndSwapChain();
	CreateRenderTargetView();
	SetViewport();

	//삼각형 그리기 파트
	CreateGeometry();
	CreateVS();
	CreateInputLayout();
	CreatePS();

	CreateSRV();
}
	//PS
	_deviceContext->PSSetShader(_pixelShader.Get(), nullptr, 0);
	_deviceContext->PSSetShaderResources(0, 1, _shaderResourceView.GetAddressOf());		//0번슬롯에 1개

 

실행화면 

 

uv 1일때 

uv 0.5일때

 

uv좌표가 1을 초과했을 때 어떻게 할지

-> 레스터라이저단계의 샘플스테이트를 통해 관리 

 

어떤게 범용적 어떤게 오브젝트에 종속적인지 생각해보기 

ex) GeoMetry - 매쉬 - 오브젝트 대상

쉐이더 - 범용적

+ Recent posts