본문 바로가기
공부/DirectX3D

[DirectX3D] RTT구현(미니맵만들기)

by MY블로그 2023. 8. 16.

D3D11 에서 RTT(Render To Texture)구현하기

RenderTarget 클래스 생성하기(h)

#pragma once
class RenderTarget
{
protected:
    // 화면(이미지)의 가로 세로 크기 변수
    int width, height; //protected에 있는 이유는 ResizeScreen으로만 크기변경하려고

    //I(인터페이스)D3D(도구)11(넘버링) 인터페이스
    ID3D11Texture2D* rgb; // 일반 텍스처 데이터
    ID3D11Texture2D* depth; // 깊이값 텍스처 데이터

    //파이프라인 결합할때 타겟으로 쓸 인터페이스
    /*
    기존에 Direct3D11 클래스에서 백버퍼로도 쓰였음(실질적자원담당)
    프로그램실행시 싱글톤 생성하면서 만들어지고,
    ResizeScreen할때 다시만들어짐(화면크기가 변동될때)
    */
    ID3D11RenderTargetView* rgbTarget;
    /*
    RenderTargetView(RTV)는 스왑체인 또는 파이프라인에 바인딩될수있는 인터페이스이다.
    2D텍스처 자원과 크게 다르지않지만 다른 역활을 하게 된다.
    */
    ID3D11DepthStencilView* depthTarget;
    /*
    깊이값이 있는지 없는지에대하여
    */

    //파이프라인 결합할때 텍스쳐로 쓸 인터페이스
    /*
    즉, 위의 자원으로 그려진 이미지를 그림으로(텍스처로 사용 하기 위한)
    */
    ID3D11ShaderResourceView* rgbResource;
    ID3D11ShaderResourceView* depthResource;

    //기본 사용할 샘플러
    ID3D11SamplerState* sampler;

    //버퍼 생성 소멸
    void CreateBackBuffer(float width, float height); // 해상도값으로 생성
    void DeleteBackBuffer();

public:
    //생성시 실행화면의 가로세로크기만큼으로 생성
    RenderTarget(UINT width = App.GetWidth(),
        UINT height = App.GetHeight());
    ~RenderTarget();

    //메소드
    void SetTarget(Color clear = Color(0, 0, 0, 1)); // 기본흰색에 투명하지않음 A가0이면 투명
    void SetRGBTexture(int slot);

    //창사이즈 조절
    void ResizeScreen(float width, float height);
};

RenderTarget 클래스 생성하기(cpp)

#include "framework.h"

void RenderTarget::CreateBackBuffer(float width, float height)
{
    //Create rgb
    HRESULT hr;
    {
        D3D11_TEXTURE2D_DESC desc = { 0 };
        desc.Width = width;
        desc.Height = height;
        desc.MipLevels = 1;
        desc.ArraySize = 1;
        desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // RGB
        desc.SampleDesc.Count = 1;
        desc.SampleDesc.Quality = 0;
        desc.Usage = D3D11_USAGE_DEFAULT;
        // 바인드플래그는 두가지용도로 넣어줄 수 있다!(다른용도들도 가능)
        desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
        hr = D3D->GetDevice()->CreateTexture2D(&desc, nullptr, &rgb); // 디바이스로 생성
        Check(hr);
    }
    //Create rgb Target 자원뷰(RTV)
    {
        D3D11_RENDER_TARGET_VIEW_DESC desc = {};
        desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
        hr = D3D->GetDevice()->CreateRenderTargetView(rgb, &desc, &rgbTarget);
        Check(hr);
    }
    //Create rgb Resource 자원뷰2(SRV)
    {
        D3D11_SHADER_RESOURCE_VIEW_DESC desc = {};
        desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
        desc.Texture2D.MipLevels = 1;

        hr = D3D->GetDevice()->CreateShaderResourceView(rgb, &desc, &rgbResource);
        Check(hr);
    }

    //Create depth Texture
    {
        D3D11_TEXTURE2D_DESC desc;
        ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
        desc.Width = this->width;
        desc.Height = this->height;
        desc.MipLevels = 1;
        desc.ArraySize = 1;
        desc.Format = DXGI_FORMAT_R32_TYPELESS; // R32_TYPELESS 단순 32비트로 만들것
        desc.SampleDesc.Count = 1;
        desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DEPTH_STENCIL;
        hr = D3D->GetDevice()->CreateTexture2D(&desc, nullptr, &depth);
        Check(hr);
    }

    {//Create DSV
        D3D11_DEPTH_STENCIL_VIEW_DESC desc;
        ZeroMemory(&desc, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));
        desc.Format = DXGI_FORMAT_D32_FLOAT; //원래백버퍼 24,8 이었으나 깊이값만쓰므로 D(depth)32비트
        desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
        desc.Texture2D.MipSlice = 0;

        hr = D3D->GetDevice()->CreateDepthStencilView(depth, &desc, &depthTarget);
        Check(hr);
    }
    //Create SRV2
    {
        D3D11_SHADER_RESOURCE_VIEW_DESC desc;
        ZeroMemory(&desc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
        desc.Format = DXGI_FORMAT_R32_FLOAT;
        desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
        desc.Texture2D.MipLevels = 1;
        hr = D3D->GetDevice()->CreateShaderResourceView(depth, &desc, &depthResource);
        Check(hr);
    }

    //샘플러
    {
        D3D11_SAMPLER_DESC SamplerDesc;

        SamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
        SamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
        SamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
        SamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
        SamplerDesc.MipLODBias = 0.0f;
        SamplerDesc.MaxAnisotropy = 1;
        SamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
        SamplerDesc.MinLOD = -FLT_MAX;
        SamplerDesc.MaxLOD = FLT_MAX;
        hr = D3D->GetDevice()->CreateSamplerState(&SamplerDesc, &sampler);
    }
}

void RenderTarget::DeleteBackBuffer()
{
    // 소멸자는 전부다 Release. 단, 생성의 역순으로 해제!
    // 동적할당은 Delete, 인터페이스해제는 Relese !
    SafeRelease(rgbTarget);
    SafeRelease(depthTarget);
    SafeRelease(rgbResource);
    SafeRelease(depthResource);
    SafeRelease(rgb);
    SafeRelease(depth);
}

RenderTarget::RenderTarget(UINT width, UINT height) // 생성자
    : width(width), height(height)
{
    CreateBackBuffer(width, height); // 백버퍼생성 함수로 실행
}

RenderTarget::~RenderTarget() // 소멸자
{
    DeleteBackBuffer(); // 백버퍼 해제 함수로 실행
}

void RenderTarget::SetTarget(Color clear) // Color = R,G,B,A
{
    D3D->SetRenderTarget(rgbTarget, depthTarget); //사용할 렌더타겟을 바인딩하는 함수
    D3D->Clear(clear, rgbTarget, depthTarget);
    //클리어함수는 기존렌더를 없애고 새로운 렌더를 그려야하기때문이다.
    //잔상효과등은 클리어를 사용하지 않는것으로 사용이 가능할수도?
}

void RenderTarget::SetRGBTexture(int slot)
{
    D3D->GetDC()->PSSetShaderResources(slot, 1, &rgbResource);//(연결될레지스터번호,리소스갯수,&rgbResource)
    D3D->GetDC()->PSSetSamplers(slot, 1, &sampler); // 샘플러는 기본 샘플러 사용
}

void RenderTarget::ResizeScreen(float width, float height) // 화면크기 재조정
{
    if (width < 1 || height < 1)
        return;

    this->width = width;
    this->height = height;

    DeleteBackBuffer();
    CreateBackBuffer(width, height);
    /*
    Delete 이후 Create를 바로 반복하는 이유는
    메모리는 이차원적이 아니고 일차원적으로 배얼처럼 이루어져있는데
    가로세로 100의 백버퍼에서 첫번째줄이아닌 두번째 줄에접근시
    시작부분+크기(가로)만큼 더해주면 2번째줄이 되는데
    사이즈가 변동될경우 접근이 까다로워지기때문에
    차라리 버퍼를제거(Delete)해준뒤 변동된 크기의 버퍼로 생성(Create)
    해주는것이 더 효율적 이기 때문이다.
    */
}

PostEffect 쉐이더 파일 생성하기(hlsl)

#include "Common.hlsl"
//RTT는 이미지를 사용하기 때문에 샘플러와 Uv를 사용
struct VertexInput
{
    float4 Position : POSITION0;
    float2 Uv : UV0;
};
struct PixelInput
{
    float4 Position : SV_POSITION;
    float2 Uv : UV0;
};

PixelInput VS(VertexInput input)
{
   
    PixelInput output;
    output.Uv = input.Uv;
    output.Position = mul(input.Position, World);
    return output;
}

float4 PS(PixelInput input) : SV_TARGET
{
	float4 BaseColor = TextureD.Sample(SamplerD, input.Uv);
    //if (BaseColor.a == 0) discard;
  
    return BaseColor;
}

RTT를 사용하여 미니맵 효과 적용하기

RTT를 사용하여 미니맵을 만들어 보려 합니다.

가장 주의해야될 것은 렌더구간을 나누어 주는 것과 렌더의 순서 입니다.

렌더는 2개의 함수로 나누어 사용하였습니다. (기본 Render(), PreRender()의 2개함수로 구간을 나눔)

또한 기본(main) 카메라 외 미니맵(전체맵을 투영후 축소)만들어줄 추가적인 카메라가 필요합니다.

void Scene2::Render() // 메인화면 출력용 렌더
{
	Camera::main->Set();
    ... 객체마다의 렌더 함수들 생략 ...
    
    객체들의 렌더 들이 완료된후
    RTT객체를 통하여 텍스처를 만들어줍니다.
    RTT->SetRGBTexture(1);
    postEffect->Render(); // 꼭 최후에 렌더를 진행합니다.
}

void Scene2::PreRender() // 미니맵 렌더용 함수
{
	RTT->SetTarget(Color(0, 0, 0, 0)); // 타겟을 변경
    cam2->Set(); // 메인카메라외 전체맵을 비출 추가의 카메라입니다.
    ... 미니맵에 출력할객체들의 렌더 코드생략 ...
}

아래의 이미지는 전체적으로 어두운 맵과

어둡지 않은 미니맵을 실제 적용시켜본 예시 입니다.

미니맵을 비추는 카메라는 직교투영을 사용하여 입체감을 제거하였습니다.

플레이어의 현재위치에 아이콘(붉은색  해골마크)의 위치가 실시간 연동됩니다.

 

댓글