08.29

신승빈·2022년 8월 29일
0

KGCA 수업

목록 보기
31/128

3D 기초

왼소좌표계 - 오른손좌표계

왼손 오른손 각 손가락이 가리키는 좌표축은 동일
x, y축을 정렬하면 왼손좌표계의 z축은 정면 방향을, 오른손좌표계의 z축은 후면 방향을 가리킴

위치벡터, 기저벡터

임의의 위치벡터는 기저벡터의 조합으로 이루어짐

3D 그래픽스 절차

모델링

모델 제작 프로그램(3D MAX 등)마다 고유 좌표계 존재
모델과 엔진의 좌표에 대한 변환 필요

종류

Wireframe : 대상을 선 정보만으로 표현
Polygon : Primitive 집합으로 표현
Solid : 모양 외에 다양한 성질을 표현

생성방법

폴리곤 메쉬
매개변수 : 베지어, 스플라인 등
스위핑 : 평면도형이 지나가는 공간의 볼륨
솔리드 : 기본 객체의 집합(합, 차)
프랙탈
파티클

투영

World에 표현된 Object를 2차원 평면에 투영하는 과정
기하변환, 좌표계 변환, 투영 변환 등(그래픽 렌더링 파이프라인)
일반적으로 World Object는 원근투영, UI는 직교투영

렌더링

은면제거

가려진 객체나 면을 제거하는 과정

세이딩

색상, 명암, 입체감 부여

컬러모델

RGB, CMY, HSV

색상과 음영 처리 방법

Flat 쉐이딩, Gouraud 쉐이딩, phong 쉐이딩

빛 계산

Diffuse 조명, Specular Reflection, 빛의 굴절(Refraction)

텍스쳐 매핑

Object의 표면에 이미지 붙이기

뷰포트

장면 연출 영역 중 일정 범위만 출력 장치에 표현하는 것
Window client 일부 영역만 출력도 가능

clipping

뷰포트를 벗어나는 부분을 제외하는 알고리즘

렌더링 파이프라인


사각형 단계는 코딩 불가능 - 원형 단계는 코딩 가능
Hull, Tessellation, Domain은 Tessellation 단계라고 칭함
Tesselation 단계와 Geomtry Stage를 설정하지 않아도 출력 가능
Rasterizer : Object에 투영된 NDC가 Display될 때 어느 픽셀에 대응되야 하는지 결정

NDC(Normalized Device Coordinate)

화면에 독립적인 출력 좌표계
x, y축은 -1 ~ 1사이
z축은 openGL의 경우 -1 ~ 1, directx의 경우 0 ~ 1 사이

Vertex Buffer

정점 정보가 저장된 버퍼
GPU 메모리에 저장되야 함

예제

struct SimpleVertex
{
	float  x; // 위치
	float  y;
	float  z;
	float  r; // 컬러
	float  g;
	float  b;
	float  a;
};
bool CraeteVertexBuffer
{
    HRESULT hr;
    SimpleVertex vertices[] =
    {
        -1.0f, 1.0f,  0.0f, 0.0f,0.0f,0.0f,0.0f,// v0
        +1.0f, 1.0f,  0.0f, 0.0f,0.0f,0.0f,0.0f,// v1
        -1.0f, -1.0f, 0.0f, 0.0f,0.0f,0.0f,0.0f,// v2
    };
    D3D11_BUFFER_DESC       bd;
    ZeroMemory(&bd, sizeof(bd));
    bd.ByteWidth    = sizeof(SimpleVertex)*3; // 바이트 용량
    // GPU 메모리에 할당
    bd.Usage        = D3D11_USAGE_DEFAULT; // 버퍼의 할당 장소 내지는 버퍼용도
    bd.BindFlags    = D3D11_BIND_VERTEX_BUFFER;
    
    D3D11_SUBRESOURCE_DATA  sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.pSysMem = vertices;    
    // 버퍼 생성
    hr=m_pd3dDevice->CreateBuffer(
        &bd, // 버퍼 할당
        &sd, // 초기 할당된 버퍼를 체우는 CPU메모리 주소
        &m_pVertexBuffer);

    // 정점쉐이더 컴파일 
    ID3DBlob* pErrorCode = nullptr;
    // cpp 파일은 유니코드를 지원하지만 쉐이더 스크립트는 유니코드를 지원하지 않음
    // #include <d3dcompiler.h>
    // #pragma comment(lib, "d3dcompiler.lib")
    hr = D3DCompileFromFile(
        L"VertexShader.txt",	// 쉐이터 스크립트 파일 이름
        NULL,
        NULL,
        "main",					// 쉐이더 스크립트의 시작 함수 이름
        "vs_5_0",				// 컴파일할 쉐이더 버전
        0,
        0,
        &m_pVSCode,				// 컴파일 된 Blob 코드
        &pErrorCode);			// 에러 발생 시 반환 코드
    if (FAILED(hr))
    {
        if (pErrorCode)
        {
            OutputDebugStringA((char*)pErrorCode->GetBufferPointer());
            // Release 수행하지 않을 시 메모리 누수 발생
            pErrorCode->Release();
        }
        return false;
    }
    // 생성된 쉐이더 코드 Blob으로 쉐이더 생성
    hr = m_pd3dDevice->CreateVertexShader(
        m_pVSCode->GetBufferPointer(),
        m_pVSCode->GetBufferSize(),
        NULL,
        &m_pVS);

    // 픽쉘쉐이더 컴파일  
    hr = D3DCompileFromFile(
        L"PixelShader.txt",
        NULL,
        NULL,
        "PSMain",
        "ps_5_0",
        0,
        0,
        &m_pPSCode,
        &pErrorCode);
    if (FAILED(hr))
    {
        if (pErrorCode)
        {
        	// Visual Studio 출력 창에 출력하는 함수
            OutputDebugStringA((char*)pErrorCode->GetBufferPointer());
            pErrorCode->Release();
        }
        return false;
    }
    hr = m_pd3dDevice->CreatePixelShader(
        m_pPSCode->GetBufferPointer(),
        m_pPSCode->GetBufferSize(),
        NULL,
        &m_pPS);
    if (FAILED(hr))
    {
        if (pErrorCode)
        {
            OutputDebugStringA((char*)pErrorCode->GetBufferPointer());
            pErrorCode->Release();
        }
        return false;
    }
    // 정점레이아웃은 정점쉐이더 밀접한 관련
    // 정점레이아웃 생성시 사전에 정점쉐이더 생성이 필요
    D3D11_INPUT_ELEMENT_DESC ied []=
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,0,D3D11_INPUT_PER_VERTEX_DATA, 0},
        { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,12,D3D11_INPUT_PER_VERTEX_DATA, 0},
    };
    UINT NumElements = sizeof(ied) / sizeof(ied[0]);
    hr = m_pd3dDevice->CreateInputLayout(
        ied,
        NumElements,
        m_pVSCode->GetBufferPointer(),
        m_pVSCode->GetBufferSize(),
         &m_pVertexLayout);
    
    return hr;
}
    bool		Sample::Render()
{
    // 삼각형 랜더링
    UINT stride = sizeof(SimpleVertex); // 정점1개의 바이트용량
    UINT offset = 0; // 정점버퍼에서 출발지점(바이트)
    //SLOT(레지스터리)
    m_pImmediateContext->IASetVertexBuffers(0, 1,
        &m_pVertexBuffer, &stride, &offset);
    m_pImmediateContext->IASetInputLayout(m_pVertexLayout);
    m_pImmediateContext->VSSetShader(m_pVS, NULL, 0);
    m_pImmediateContext->PSSetShader(m_pPS, NULL, 0);
    m_pImmediateContext->Draw(3, 0);
    return true;
}
// 버퍼 설명 구조체
typedef struct D3D11_BUFFER_DESC
    {
    UINT ByteWidth;				// 버퍼의 전체 크기
    D3D11_USAGE Usage;			// 버퍼에 접근가능 유형(GPU, CPU) 및 읽기 쓰기 설정
    UINT BindFlags;				// 버퍼와 파이프라인 결합 방식(정점버퍼, 인덱스버퍼, depth-stencil 버퍼 등)
    UINT CPUAccessFlags;		// CPU 접근 허용 여부
    UINT MiscFlags;				// 기타 플래그
    UINT StructureByteStride;	// buffer가 structuredBuffer일 경우 각 element의 크기
    } 	D3D11_BUFFER_DESC;
Reference : https://docs.microsoft.com/ko-KR/windows/win32/api/d3d11/ns-d3d11-d3d11_buffer_desc
// 버퍼 초기 데이터
typedef struct D3D11_SUBRESOURCE_DATA
    {
    const void *pSysMem;		// 초기 데이터 주소
    UINT SysMemPitch;			// 2D, 3D Texture에서만 사용. 텍스처 한 줄의 크기
    UINT SysMemSlicePitch;		// 3D Texture에서만 사용. 텍스처 한 층의 크기?
    } 	D3D11_SUBRESOURCE_DATA;
Reference : https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_subresource_data
typedef struct D3D11_INPUT_ELEMENT_DESC
    {
    LPCSTR SemanticName;						// 쉐이더 스크립트로 넘어가는 이름
    UINT SemanticIndex;							// 동일한 Semantic이 있을 경우 사용
    DXGI_FORMAT Format;							// DXGI_FORMAT 사용
    UINT InputSlot;								// Input Assembler 번호
    UINT AlignedByteOffset;						// 하나의 Element에 다수의 정보가 저장되어 있을 시 원하는 데이터의 시작 주소 위치
    D3D11_INPUT_CLASSIFICATION InputSlotClass;	// 입력 데이터의 종류, input slot마다 지정
    UINT InstanceDataStepRate;					// 위 데이터가 per_instance 시 설정
    } 	D3D11_INPUT_ELEMENT_DESC;
Reference : https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_input_element_desc

쉐이더

정점쉐이더

정점 버퍼에 있는 모든 정점에 대해 한번씩 작업 수행

예제

float4 main(float3 p : POSI1TION ) : SV_POSITION
{
	//  px,py,pz, 1.0f
	return float4(p,1.0f);
}

HLSL

대소문자 구분 안함
고유 Sementic은 반환 타입이 지정되어 있음

Reference : https://docs.microsoft.com/ko-kr/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics#system-value-semantics

Sementic은 D3D11_INPUT_ELEMENT_DESC에서 지정한 값과 동일해야 함
레이아웃을 생성하기 이전에 쉐이더가 미리 컴파일 되어있어야 함
디버깅하기 위해서는 특별한 유틸리티 필요(Error용 Blob사용 추천)

픽셀쉐이더

모든 픽셀에 대해 한번씩 작업 수행

float4 PSMain(float4 p : SV_POSITION, float4 color : COLOR0) : SV_Target
{
	return c;
}

DeviceContext->IASetPrimitiveTopology

입력된 정점들을 어떤 식으로 출력할 것인가에 대한 결정
List형태의 경우 짝이 맞을 경우에만 출력(직선 2개씩, 삼각형 3개씩)
삼각형의 경우 시계방향의 순서로 주어져야지만 정면으로 판단하고 출력

profile
이상을 길잡이 삼아 로망을 추구합니다.

0개의 댓글