프로그래밍 공부
작성일
2023. 12. 13. 01:26
작성자
WDmil
728x90

우리가 카메라로 화면을 비추는것. 화면을 2D리소스로 활용할 수 있다.

 

이러한 RenderTarget은 화면 뷰에 대한 재처리작업을 통해 더 효율적으로 어둡게 만들거나, 색감을 전체적으로 조정하거나 하는데 사용한다.

 

외곽선처리또한가능하다.

 

원래 FrameWork에 추가하여 수정해보자.

 

우선, RenderTager과 DepthStencil을 정의해주는 함수를 작성한다.

 

#pragma once

// RenderTarget 클래스 정의
class RenderTarget
{
public:
    // 생성자: 너비와 높이를 매개변수로 받아 기본값으로 초기화
    RenderTarget(UINT width = WIN_WIDTH, UINT height = WIN_HEIGHT);

    // 소멸자
    ~RenderTarget();

    // 렌더 타겟 설정 함수
    void Set(DepthStencil* depthStencil, Float4 clearColor = Float4(1, 1, 1, 1));

    // 렌더 타겟의 렌더 타겟 뷰를 반환하는 함수
    ID3D11RenderTargetView*& GetRTV() { return rtv; }

    // 렌더 타겟의 셰이더 리소스 뷰를 반환하는 함수
    ID3D11ShaderResourceView*& GetSRV() { return srv; }

public:
    // 여러 렌더 타겟을 설정하는 정적 멤버 함수
    static void SetMulti(RenderTarget** targets, UINT count, DepthStencil* depthStencil, Float4 clearColor = Float4(1, 1, 1, 1));

private:
    // 렌더 타겟 텍스처를 생성하는 함수
    void CreateRTVTexture();

    // 렌더 타겟 뷰를 생성하는 함수
    void CreateRTV();

    // 셰이더 리소스 뷰를 생성하는 함수
    void CreateSRV();

private:
    // 렌더 타겟의 너비와 높이를 저장하는 변수
    UINT width, height;

    // 렌더 타겟 텍스처를 나타내는 변수
    ID3D11Texture2D* rtvTexture;

    // 렌더 타겟 뷰를 나타내는 변수
    ID3D11RenderTargetView* rtv;

    // 셰이더 리소스 뷰를 나타내는 변수
    ID3D11ShaderResourceView* srv;
};

 

RenderTarget은 현재 출력되는 메인뷰를 정의해주는 것 으로, View가 정의된 데이터를 확인하고, 해당 View의 사이즈만큼 정의된 후, 데이터를 읽어오는 역할을 한다.

 

윈도우창의 사이즈가 같아야 한다.

 

만약, 반절 사이즈를 입력하면 반절만 2Dtexture로 가져올 수 있다.

 

#include "Framework.h"

// RenderTarget 클래스의 생성자
RenderTarget::RenderTarget(UINT width, UINT height)
    : width(width), height(height)
{
    // 렌더 타겟 텍스처 생성
    CreateRTVTexture();

    // 렌더 타겟 뷰 생성
    CreateRTV();

    // 셰이더 리소스 뷰 생성
    CreateSRV();
}

// RenderTarget 클래스의 소멸자
RenderTarget::~RenderTarget()
{
    // 생성된 자원들을 해제
    rtvTexture->Release();
    rtv->Release();
    srv->Release();
}

// 렌더 타겟과 깊이 스텐실을 설정하는 함수
void RenderTarget::Set(DepthStencil* depthStencil, Float4 clearColor)
{
    // 현재 렌더 타겟 및 깊이 스텐실을 설정
    DC->OMSetRenderTargets(1, &rtv, depthStencil->GetDSV());

    // 렌더 타겟 클리어
    DC->ClearRenderTargetView(rtv, (float*)&clearColor);

    // 깊이 스텐실 클리어
    depthStencil->Clear();

    // 환경 설정 적용
    Environment::Get()->Set();

    // 뷰포트 설정
    Environment::Get()->SetViewport(width, height);
}

// 여러 렌더 타겟을 설정하는 정적 멤버 함수
void RenderTarget::SetMulti(RenderTarget** targets, UINT count, DepthStencil* depthStencil, Float4 clearColor)
{
    // 렌더 타겟 뷰를 저장할 벡터 생성
    vector<ID3D11RenderTargetView*> rtvs;

    // 각 렌더 타겟에 대해 렌더 타겟 뷰를 벡터에 추가하고 클리어
    FOR(count)
    {
        rtvs.push_back(targets[i]->GetRTV());
        DC->ClearRenderTargetView(rtvs.back(), (float*)&clearColor);
    }

    // 깊이 스텐실 클리어
    depthStencil->Clear();

    // 현재 렌더 타겟들 및 깊이 스텐실을 설정
    DC->OMSetRenderTargets(count, rtvs.data(), depthStencil->GetDSV());

    // 환경 설정 적용
    Environment::Get()->Set();
}

// 렌더 타겟 텍스처를 생성하는 함수
void RenderTarget::CreateRTVTexture()
{
    // D3D11_TEXTURE2D_DESC 구조체 초기화
    D3D11_TEXTURE2D_DESC desc = {};
    desc.Width = width;
    desc.Height = height;
    desc.MipLevels = 1;
    desc.ArraySize = 1;
    desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc.SampleDesc.Count = 1;
    desc.Usage = D3D11_USAGE_DEFAULT;
    desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;

    // 렌더 타겟 텍스처 생성
    DEVICE->CreateTexture2D(&desc, nullptr, &rtvTexture);
}

// 렌더 타겟 뷰를 생성하는 함수
void RenderTarget::CreateRTV()
{
    // D3D11_RENDER_TARGET_VIEW_DESC 구조체 초기화
    D3D11_RENDER_TARGET_VIEW_DESC desc = {};
    desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

    // 렌더 타겟 뷰 생성
    DEVICE->CreateRenderTargetView(rtvTexture, &desc, &rtv);
}

// 셰이더 리소스 뷰를 생성하는 함수
void RenderTarget::CreateSRV()
{
    // D3D11_SHADER_RESOURCE_VIEW_DESC 구조체 초기화
    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
    srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2DArray.MipLevels = 1;

    // 셰이더 리소스 뷰 생성
    DEVICE->CreateShaderResourceView(rtvTexture, &srvDesc, &srv);
}

 

정의된 방법대로 RenderTarget을 초기화해준다.

 

각 WinAPI방식을 따른다.

 

#pragma once

// 깊이 스텐실(DepthStencil) 클래스 정의
class DepthStencil
{
public:
    // 생성자: 너비와 높이, 스텐실 사용 여부를 매개변수로 받아 기본값으로 초기화
    DepthStencil(UINT width = WIN_WIDTH, UINT height = WIN_HEIGHT, bool isStencil = false);

    // 소멸자
    ~DepthStencil();

    // 깊이 스텐실을 클리어하는 함수
    void Clear();

    // 깊이 스텐실 뷰를 반환하는 함수
    ID3D11DepthStencilView* GetDSV() { return dsv; }

    // 셰이더 리소스 뷰를 반환하는 함수
    ID3D11ShaderResourceView* GetSRV() { return srv; }

private:
    // 깊이 스텐실 텍스처를 생성하는 함수
    void CreateDSVTexture();

    // 깊이 스텐실 뷰를 생성하는 함수
    void CreateDSV();

    // 셰이더 리소스 뷰를 생성하는 함수
    void CreateSRV();

private:
    // 깊이 스텐실의 너비와 높이를 저장하는 변수
    UINT width, height;

    // 스텐실을 사용할 지 여부를 저장하는 변수
    bool isStencil;

    // 깊이 스텐실 텍스처를 나타내는 변수
    ID3D11Texture2D* dsvTexture;

    // 깊이 스텐실 뷰를 나타내는 변수
    ID3D11DepthStencilView* dsv;

    // 셰이더 리소스 뷰를 나타내는 변수
    ID3D11ShaderResourceView* srv;
};
#include "Framework.h"

// DepthStencil 클래스 생성자
DepthStencil::DepthStencil(UINT width, UINT height, bool isStencil)
    : width(width), height(height), isStencil(isStencil)
{
    // 깊이 스텐실 텍스처 생성
    CreateDSVTexture();

    // 깊이 스텐실 뷰 생성
    CreateDSV();

    // 셰이더 리소스 뷰 생성
    CreateSRV();
}

// DepthStencil 클래스 소멸자
DepthStencil::~DepthStencil()
{
    // 생성된 자원들을 해제
    dsvTexture->Release();
    dsv->Release();
    srv->Release();
}

// 깊이 스텐실을 클리어하는 함수
void DepthStencil::Clear()
{
    // 현재 깊이 스텐실 뷰 클리어
    DC->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH, 1.0f, 0);
}

// 깊이 스텐실 텍스처를 생성하는 함수
void DepthStencil::CreateDSVTexture()
{
    // D3D11_TEXTURE2D_DESC 구조체 초기화
    D3D11_TEXTURE2D_DESC desc = {};
    desc.Width = width;
    desc.Height = height;
    desc.MipLevels = 1;
    desc.ArraySize = 1;
    desc.Format = isStencil ? DXGI_FORMAT_R24G8_TYPELESS : DXGI_FORMAT_R32_TYPELESS;
    desc.SampleDesc.Count = 1;
    desc.Usage = D3D11_USAGE_DEFAULT;
    desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;

    // 깊이 스텐실 텍스처 생성
    DEVICE->CreateTexture2D(&desc, nullptr, &dsvTexture);
}

// 깊이 스텐실 뷰를 생성하는 함수
void DepthStencil::CreateDSV()
{
    // D3D11_DEPTH_STENCIL_VIEW_DESC 구조체 초기화
    D3D11_DEPTH_STENCIL_VIEW_DESC depthViewDesc = {};
    depthViewDesc.Format = isStencil ? DXGI_FORMAT_D24_UNORM_S8_UINT : DXGI_FORMAT_D32_FLOAT;
    depthViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;

    // 깊이 스텐실 뷰 생성
    DEVICE->CreateDepthStencilView(dsvTexture, &depthViewDesc, &dsv);
}

// 셰이더 리소스 뷰를 생성하는 함수
void DepthStencil::CreateSRV()
{
    // D3D11_SHADER_RESOURCE_VIEW_DESC 구조체 초기화
    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
    srvDesc.Format = isStencil ? DXGI_FORMAT_R24_UNORM_X8_TYPELESS : DXGI_FORMAT_R32_FLOAT;
    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2DArray.MipLevels = 1;

    // 셰이더 리소스 뷰 생성
    DEVICE->CreateShaderResourceView(dsvTexture, &srvDesc, &srv);
}

 

깊이뷰를 정의해주는 함수이다.

 

각 깊이뷰를 정의해주고, 해당 깊이 스텐실에 따라 텍스처를 생성한 뒤, 넘겨주는 역할을 한다.

 

우리는 렌더 정의부에서 PreRender부분에서 작업을 하고, Device를 초기화해준 다음, 다음 렌더로 넘어갈 것 이다.

 

OM의 초기화방법이 위와같이 다름으로, Clear시 OM을 초기화할 수 있도록 Deivce함수를 수정해준다.

 

void Device::Clear()
{
    deviceContext->OMSetRenderTargets(1, &renderTargetView, depthStencilView);

    float clearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f };
    deviceContext->ClearRenderTargetView(renderTargetView, clearColor);
    deviceContext->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
}

 

Device가 Clear될 때, OM의 RenderTarget을 바꾸어주어야 한다.

 

각각 다른 뷰포트와 깊이스텐실을 사용하기 때문.


정의가 다 끝났으면, 쉐이더를 만들어보자.

 

제작할 쉐이더는, Grayscale로 조정하는 쉐이더와 Sepia로 조정하는 쉐이더 이다.

 

 

#include "../VertexHeader.hlsli"
#include "../PixelHeader.hlsli"

struct PixelInput
{
	float4 pos : SV_POSITION;
	float2 uv : UV;
};

PixelInput VS(VertexUV input)
{
	PixelInput output;
	output.pos = mul(input.pos, world);
	output.pos = mul(output.pos, view);
	output.pos = mul(output.pos, projection);
	
	output.uv = input.uv;
	
	return output;
}

float4 PS(PixelInput input) : SV_TARGET
{
	float4 baseColor = diffuseMap.Sample(samp, input.uv);
	
	//float scale = (baseColor.r + baseColor.g + baseColor.b) / 3;
	//float3 grayColor;
	//grayColor.r = baseColor.r * 0.3f;
	//grayColor.g = baseColor.g * 0.59f;
	//grayColor.b = baseColor.b * 0.11f;
	//
	//float scale = grayColor.r + grayColor.g + grayColor.b;
	
	float scale = dot(baseColor.rgb, float3(0.3f, 0.59f, 0.11f));
	
	return float4(scale.xxx, 1.0f) * mDiffuse;
}
#include "../VertexHeader.hlsli"
#include "../PixelHeader.hlsli"

struct PixelInput
{
	float4 pos : SV_POSITION;
	float2 uv : UV;
};

PixelInput VS(VertexUV input)
{
	PixelInput output;
	output.pos = mul(input.pos, world);
	output.pos = mul(output.pos, view);
	output.pos = mul(output.pos, projection);
	
	output.uv = input.uv;
	
	return output;
}

float4 PS(PixelInput input) : SV_TARGET
{
	float4 baseColor = diffuseMap.Sample(samp, input.uv);
	
	float4 sepia = 1;
	
	sepia.r = dot(baseColor.rgb, float3(0.393f, 0.769f, 0.189f));
	sepia.g = dot(baseColor.rgb, float3(0.349f, 0.646f, 0.168f));
	sepia.b = dot(baseColor.rgb, float3(0.272f, 0.534f, 0.131f));
	
	return sepia * mDiffuse;
}

 

각각 그레이스케일과 세피아로 조정해주는 쉐이더이다.

 

rgb에 따른 객체데이터를 일정비율로 조정해주는 것 은. 3개의 색갈이 전부 같게 나와야 하기 때문에, 벡터의 내적과 같은 결과물이 나타난다.

 

그럼으로, 내적으로 계산해준다.

#include "Framework.h"
#include "RenderTargetScene.h"

RenderTargetScene::RenderTargetScene()
{
    CreateObjects();

    renderTarget = new RenderTarget();
    depthStencil = new DepthStencil();

    Texture* target = Texture::Add(L"Target", renderTarget->GetSRV());
    targetQuad = new Quad(Float2(WIN_WIDTH, WIN_HEIGHT));
    //targetQuad = new Quad();
    targetQuad->GetMaterial()->SetShader(L"PostEffect/Sepia.hlsl");
    targetQuad->GetMaterial()->SetDiffuseMap(target);

    targetQuad->SetLocalPosition({ CENTER_X, CENTER_Y, 0 });
    targetQuad->UpdateWorld();
}

RenderTargetScene::~RenderTargetScene()
{
    delete quad;
    delete model;
    delete sphere;

    delete targetQuad;
}

void RenderTargetScene::Update()
{
    quad->UpdateWorld();
    model->UpdateWorld();
    sphere->UpdateWorld();
}

void RenderTargetScene::PreRender()
{
    renderTarget->Set(depthStencil);

    quad->Render();
    model->Render();
    sphere->Render();
}

void RenderTargetScene::Render()
{

}

void RenderTargetScene::PostRender()
{
   targetQuad->Render();
}

void RenderTargetScene::GUIRender()
{
    //quad->GUIRender();
    //model->GUIRender();
    //sphere->GUIRender();
    //sphere->GetMaterial()->GUIRender();

    targetQuad->GetMaterial()->GUIRender();
}

void RenderTargetScene::CreateObjects()
{
    quad = new Quad();
    quad->Load();

    model = new Model("StanfordBunny");
    model->Load();

    sphere = new Sphere();
    //sphere->GetMaterial()->Load("TextData/Materials/FieldStone.mat");
    sphere->Load();
}

 

이제, scene를 구상하여, 렌더타입을 조정해준다.

 

우리는 PreRender에서 데이터를 렌더하고, targetQuad를 PostRender를 통해 View에 깊이값을 적용하지 않은 상태에서 렌더 시켜줄 것 이다.

 

 

Gray

 

Sepia

 

728x90