필요한 개념
TextureBuffer->2차원 배열, 3차원 배열로 가능
이미지를 CS로 넘겨서 연산을 수행하도록 한다.
스레드 ID랑 맞춰서 쓸수있도록 32개씩 쪼개고 그룹이 여러개가 생기고 그룹전체에서 스레드ID(dispatchThreadID) 맞춘다.
이미지 Width/32로 나누고 +1함
// 스레드 그룹의 갯수를 구함
float x = ((float)width / 32) < 1.0f ? 1.0f : ((float)width / 32);
float y = ((float)height / 32) < 1.0f ? 1.0f : ((float)height / 32);
// 100픽셀이 라고 하면 100/32 = 3.125인데 0.125부분이 짤려서 96픽셀 밖에 안나올 것이다.
// 그래서 ceil()로 소수점이 있다면 무조건 올림 처리 해준다.
// ex) 3.125 -> 4가 된다.
// 즉 4픽셀을 제외한 나머지 28픽셀 스레드 부분은 처리 안하면 그만이다.
shader->Dispatch(0, 0, (UINT)ceil(x), (UINT)ceil(y), arraySize);
*DispatchThreadID는 그룹에 상관없이 자신의 스레드 방향으로 계속 증가하므로, 결국 픽셀의 값이랑 일치하게 된다.
== 나중에 배울것==
화면을 렌더타켓을 텍스쳐로 빼내서 화면을 조작하는 기법을 실제 게임화면을 다시한번 후에 처리한다해서 포스트 프로세싱이라고 부른다.
포스트 프로세싱(PostEffect) : 실시간으로 렌더링 되는 화면을 다시 픽셀 세이더로 넘겨서 화면에 효과를 주는 방식(피가 딸았을때 붉은 빛을 입힌 다던가,
가속을 할때 빠른 효과를 내기위해 모션블러를 준다던가, 우리가 지금 처리한 기법과 비슷하다.)
RawBuffer & TextureBuffer 코드 구조 분석

- RawBuffer는 따로 result 리소스를 생성해서 출력용으로 사용하고 있다.
- 반면, TextureBuffer는 output을 그대로 shader로 넘겨줌(다시 SRV를 통해 Shader로 넘어갈 수 있도록 세팅(계산 완료된 2D Texture를 outputSRV로 해서 다른 Render2D에 넘기기 위해))
Buffers.h 추가된 내용
...
///////////////////////////////////////////////////////////////////////////////
class TextureBuffer : public CsResource
{
public:
TextureBuffer(ID3D11Texture2D* src);
~TextureBuffer();
private:
void CreateSRV() override;
void CreateOutput() override;
void CreateUAV() override;
void CreateResult() override;
public:
UINT Width() { return width; }
UINT Height() { return height; }
UINT ArraySize() { return arraySize; }
ID3D11Texture2D* Output() { return (ID3D11Texture2D*)output; }
ID3D11ShaderResourceView* OutputSRV() { return outputSRV; }
private:
UINT width, height, arraySize;
DXGI_FORMAT format;
ID3D11ShaderResourceView* outputSRV;
};
Buffers.cpp 추가된 내용
...
///////////////////////////////////////////////////////////////////////////////
TextureBuffer::TextureBuffer(ID3D11Texture2D* src)
{
D3D11_TEXTURE2D_DESC srcDesc;
src->GetDesc(&srcDesc);
width = srcDesc.Width;
height = srcDesc.Height;
arraySize = srcDesc.ArraySize;
format = srcDesc.Format;
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
desc.Width = width;
desc.Height = height;
desc.ArraySize = arraySize;
desc.Format = format;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
ID3D11Texture2D* texture = NULL;
Check(D3D::GetDevice()->CreateTexture2D(&desc, NULL, &texture));
D3D::GetDC()->CopyResource(texture, src);
input = (ID3D11Resource*)texture;
CreateBuffer();
}
TextureBuffer::~TextureBuffer()
{
}
void TextureBuffer::CreateSRV()
{
ID3D11Texture2D* texture = (ID3D11Texture2D*)input;
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
srvDesc.Format = desc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srvDesc.Texture2DArray.MipLevels = 1;
// 스레드 그룹이 x,y,z이었다. z가 있을 경우 arraySize를 준다.
// Texture2Darray == Texture3D
srvDesc.Texture2DArray.ArraySize = arraySize;
Check(D3D::GetDevice()->CreateShaderResourceView(texture, &srvDesc, &srv));
}
void TextureBuffer::CreateOutput()
{
ID3D11Texture2D* texture = NULL;
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
desc.Width = width;
desc.Height = height;
desc.ArraySize = arraySize;
desc.Format = format;
// UAV와 SRV을 연결(Output 자체를 SRV를 사용)
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
Check(D3D::GetDevice()->CreateTexture2D(&desc, NULL, &texture));
output = (ID3D11Resource*)texture;
}
void TextureBuffer::CreateUAV()
{
ID3D11Texture2D* texture = (ID3D11Texture2D*)output;
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
ZeroMemory(&uavDesc, sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC));
uavDesc.Format = DXGI_FORMAT_UNKNOWN;
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
uavDesc.Texture2DArray.ArraySize = arraySize;
Check(D3D::GetDevice()->CreateUnorderedAccessView(texture, &uavDesc, &uav));
}
void TextureBuffer::CreateResult()
{
ID3D11Texture2D* texture = (ID3D11Texture2D*)output;
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
// 포맷은 그래도 가져다 씀
srvDesc.Format = desc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srvDesc.Texture2DArray.MipLevels = 1;
srvDesc.Texture2DArray.ArraySize = arraySize;
// Result는 다시 SRV를 통해 Shader로 넘어갈 수 있도록 세팅
Check(D3D::GetDevice()->CreateShaderResourceView(texture, &srvDesc, &outputSRV));
}
Render2D.h
#pragma once
// Texture 화면에 보여주는 클래스
class Render2D : public Renderer
{
public:
Render2D();
~Render2D();
void Update();
void Render();
void SRV(ID3D11ShaderResourceView* srv);
private:
struct Desc
{
Matrix View;
Matrix Projection;
} desc;
private:
ConstantBuffer* buffer;
ID3DX11EffectShaderResourceVariable* sDiffuseMap;
};
Render2D.cpp
#include "Framework.h"
#include "Render2D.h"
Render2D::Render2D()
: Renderer(L"62_Render2D.fx")
{
// View는 -1 ~ 0 지점 전방 바라볼 수 있도록 세팅, 그렇게 하면 수직은 y가 1이 됨
D3DXMatrixLookAtLH(&desc.View, &Vector3(0, 0, -1), &Vector3(0, 0, 0), &Vector3(0, 1, 0));
// Projection 세팅
// D3DXMatrixOrthographics - 직교 투영함수
// 원근 투영(Perspective) - 물체가 멀면 멀수록 작게 보임 -> 우리가 써왔던
// 직교 투영(Orthographic) - 거리에 따른 보이는 크기 차이가 없다.(거리가 다른데 각각 크기가 동일해보임) -> UI 같은 것에서 처리(작아지면 안되니까)
// 2번 : left, 3번 : right, 4번 : 위, 5번 아래, 6번, 7번 : 깊이..(near, far)
// 이거는 세팅이 간단함
// 2D 보여줄 꺼니깐, 거리에 상관없도록 Orthographic으로 세팅한것
D3DXMatrixOrthoOffCenterLH(&desc.Projection, 0, D3D::Width(), 0, D3D::Height(), -1, +1);
// OrthoLH : 크기만으로 중앙점 잡음
// OrthoOffCenterLH : 중앙점 임의로 설정
buffer = new ConstantBuffer(&desc, sizeof(Desc));
shader->AsConstantBuffer("CB_Render2D")->SetConstantBuffer(buffer->Buffer());
VertexTexture vertices[6];
vertices[0].Position = Vector3(-0.5f, -0.5f, 0.0f);
vertices[1].Position = Vector3(-0.5f, +0.5f, 0.0f);
vertices[2].Position = Vector3(+0.5f, -0.5f, 0.0f);
vertices[3].Position = Vector3(+0.5f, -0.5f, 0.0f);
vertices[4].Position = Vector3(-0.5f, +0.5f, 0.0f);
vertices[5].Position = Vector3(+0.5f, +0.5f, 0.0f);
vertices[0].Uv = Vector2(0, 1);
vertices[1].Uv = Vector2(0, 0);
vertices[2].Uv = Vector2(1, 1);
vertices[3].Uv = Vector2(1, 1);
vertices[4].Uv = Vector2(0, 0);
vertices[5].Uv = Vector2(1, 0);
vertexBuffer = new VertexBuffer(vertices, 6, sizeof(VertexTexture));
sDiffuseMap = shader->AsSRV("DiffuseMap");
}
Render2D::~Render2D()
{
SafeDelete(buffer);
}
void Render2D::Update()
{
Super::Update();
}
void Render2D::Render()
{
Super::Render();
buffer->Render();
shader->Draw(0, Pass(), 6);
}
void Render2D::SRV(ID3D11ShaderResourceView* srv)
{
sDiffuseMap->SetResource(srv);
}
TextureBufferDemo.h
#pragma once
#include "Systems/IExecute.h"
class TextureBufferDemo : public IExecute
{
public:
virtual void Initialize() override;
virtual void Ready() override {};
virtual void Destroy() override {};
virtual void Update() override;
virtual void PreRender() override {};
virtual void Render() override;
// 맨뒤에서 그린다는 얘기
// 깊이를 끄고 그리는 애들(UI 등) 들은 PostRender()에서 최종적으로 그려주는게
// 좋다.
virtual void PostRender() override;
virtual void ResizeScreen() override {};
private:
Texture* texture;
TextureBuffer* textureBuffer;
Render2D* render2D;
};
TextureBufferDemo.cpp
#include "stdafx.h"
#include "TextureBufferDemo.h"
void TextureBufferDemo::Initialize()
{
Shader* shader = new Shader(L"62_TextureBuffer.fx");
// 이미지를 조작해서 화면에 보이도록 하기
texture = new Texture(L"Environment/Compute.png");
render2D = new Render2D();
// 이미지 크기
render2D->GetTransform()->Scale(D3D::Width(), D3D::Height(), 1);
// 중앙점이 0
render2D->GetTransform()->Position(D3D::Width() * 0.5f, D3D::Height() * 0.5f, 0);
// 생성자에서 ID3D11Texture를 받음
textureBuffer = new TextureBuffer(texture->GetTexture());
// 입력은 항상 As붙음
shader->AsSRV("Input")->SetResource(textureBuffer->SRV());
shader->AsUAV("Output")->SetUnorderedAccessView(textureBuffer->UAV());
// 이제 ID 계산
// textureBuffer의 width, height는 원본 버퍼의 width, height를 그대로 저장된것
UINT width = textureBuffer->Width();
UINT height = textureBuffer->Height();
// 1일 것이다.
UINT arraySize = textureBuffer->ArraySize();
// 그룹의 갯수를 구함
float x = ((float)width / 32) < 1.0f ? 1.0f : ((float)width / 32);
float y = ((float)height / 32) < 1.0f ? 1.0f : ((float)height / 32);
// 100픽셀이 라고 하면 100/32 = 3.125인데 0.125부분이 짤려서 96픽셀 밖에 안나올 것이다.
// 그래서 ceil()로 소수점이 있다면 무조건 올림 처리 해준다.
// ex) 3.125 -> 4가 된다.
// 즉 4픽셀을 제외한 나머지 28픽셀 스레드 부분은 처리 안하면 그만이다.
shader->Dispatch(0, 0, (UINT)ceil(x), (UINT)ceil(y), arraySize);
// 그냥 SRV()하면 입력용이 들어와서 OutputSRV()로 해준다.
render2D->SRV(textureBuffer->OutputSRV());
}
void TextureBufferDemo::Update()
{
render2D->Update();
}
void TextureBufferDemo::Render()
{
}
void TextureBufferDemo::PostRender()
{
render2D->Render();
}
62_TextureBuffer.fx
// float4 자료형으로 다룬다.
Texture2DArray<float4> Input;
// Output은 RW가 붙는다.
RWTexture2DArray<float4> Output;
// 최대 1024개이다.
// SV_DispatchThreadID : 스레드 그룹내에서 전체 번호
// 들어오는 SV_DispatchThreadID는 어차피 계속 증가되서 들어온다.
// 텍스쳐를 쪼개었다. 결국 스레드의 ID와 텍스쳐의 픽셀이 일치함
[numthreads(32, 32, 1)]
void CS(uint3 id : SV_DispatchThreadID)
{
// Texture2DArray Load는 ByteAddressBuffer와는 다르게 int4개가 들어간다.
// x,y,z은 id 넣어주면 된다.(읽어들일 픽셀의 주소), w는 민맵(1개만 쓰니까 0)
float4 color = Input.Load(int4(id, 0));
// 색상 바꿔봄(컬러 반전됨)
// Output도 픽셀과 일치해서
// Output[id] = 1.0f - color;
// 흑백화
// 세 값의 픽셀의 평균내서 넣어줌
Output[id] = (color.r + color.g + color.b) / 3.0f;
}
technique11 T0
{
pass P0
{
SetVertexShader(NULL);
SetPixelShader(NULL);
SetComputeShader(CompileShader(cs_5_0, CS()));
}
}
결과
'DirectX11 3D > 기본 문법' 카테고리의 다른 글
<DirectX11 3D> 66 - Viewport (0) | 2022.03.01 |
---|---|
<DirectX11 3D> 63 - ComputeShader(CS)(3) - StructuredBuffer를 이용한 GetAnimationBone (0) | 2022.03.01 |
<DirectX11 3D> 59 - ComputeShader(CS)(1) - RawBuffer(ByteAdress) (0) | 2022.02.24 |
<DirectX11 3D> 58 - ComputeShader(CS) 이론 (0) | 2022.02.24 |
<DirectX11 3D> 56 - Thread(스레드) (0) | 2022.02.23 |