응애맘마조

230713 강의 본문

공부/3D강의

230713 강의

TH.Wert 2023. 7. 13. 19:16

어제 터레인을 다 끝내지 못했는데 오늘 끝낼 수 있게 되었습니다.

#pragma once

class Terrain : public Actor
{
    struct InputDesc
    {
        UINT index;
        Vector3 v0, v1, v2;
    };
    
    struct OutputDesc
    {
        int picked;
        float u, v, distance;
    };
    
    struct RayDesc
    {
        Vector3 position;
        float size;
        Vector3 direction;
        float padding;
    };
    
    static ID3D11ComputeShader* computeShader;
public:
    static void		CreateStaticMember();
    static void		DeleteStaticMember();
    static Terrain* Create(string Name = "Terrain", int	terrainSize = 257, float uvScale = 1.0f);
    
private:
    //compute Input
    InputDesc* inputArray;
    ID3D11Resource* input;
    ID3D11ShaderResourceView* srv = nullptr;//읽기전용
    
    //compute Output
    OutputDesc* outputArray;
    ID3D11Resource* output;
    ID3D11UnorderedAccessView* uav;//읽기쓰기 둘다가능
    
    //copy용
    ID3D11Resource* result;
    
    //ray
    RayDesc                     ray;
    ID3D11Buffer*               rayBuffer;
public:
    Terrain(string Name);

    float                       uvScale;
    int                         triSize;
    void	        RenderDetail();
    void            UpdateMeshUv();
    void            CreateMesh(int Size = 257);
    //CS
    void            CreateStructuredBuffer();
    void            DeleteStructuredBuffer();

    bool            ComPutePicking(Ray WRay, OUT Vector3& HitPoint);
    void            Render() override;
};
#include "framework.h"
ID3D11ComputeShader* Terrain::computeShader = nullptr;

void Terrain::CreateStaticMember()
{
    ID3D10Blob* CsBlob;

    wstring path = L"../Shaders/ComputeShader.hlsl";

    DWORD flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_DEBUG;

    D3DCompileFromFile(path.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE,
        "CS", "cs_5_0", flags, 0, &CsBlob, nullptr);

    D3D->GetDevice()->CreateComputeShader(CsBlob->GetBufferPointer(), CsBlob->GetBufferSize(),
        nullptr, &computeShader);
}

void Terrain::DeleteStaticMember()
{
    SafeRelease(computeShader);
}

Terrain* Terrain::Create(string Name, int terrainSize, float uvScale)
{
    Terrain* result = new Terrain(Name);
    result->uvScale = uvScale;
    result->triSize = (terrainSize - 1) * (terrainSize - 1) * 2;
    result->shader = RESOURCE->shaders.Load("5.Cube.hlsl");
    result->CreateMesh(terrainSize);
    result->material = new Material();
    result->material->diffuseMap = RESOURCE->textures.Load("20000001.png");
    result->material->diffuse.w = 1.0f;

    return result;
}

Terrain::Terrain(string Name)
    : Actor()
{
    type = ObType::Terrain;
    name = Name;
    uvScale = 1.0f;
}

void Terrain::CreateMesh(int Size)
{
    VertexTerrain* vertices;
    UINT* indices;
    UINT vertexCount;
    UINT indexCount;
    vector<VertexTerrain> VecVertex;
    vector<UINT> Vecindex;

    triSize = (Size - 1) * (Size - 1) * 2;
    float half = Size * 0.5f;

    for (int z = 0; z < Size; z++)
    {
        for (int x = 0; x < Size; x++)
        {
            //배열에 들어갈 인덱스
            int Index = (z * Size) + x;
            float _x = x - half;
            float _z = -z + half;
            float _y = 0.0f;

            float _u = float(x) * uvScale / float(Size - 1);
            float _v = float(z) * uvScale / float(Size - 1);
            VertexTerrain temp;
            temp.uv = Vector2(_u, _v);
            temp.position = Vector3(_x, _y, _z);
            temp.weights.x = 1.0f;
            temp.normal = Vector3(0, 1, 0);
            VecVertex.push_back(temp);
        }
    }

    vertexCount = (UINT)VecVertex.size();
    vertices = new VertexTerrain[vertexCount];

    //사각형 갯수만큼 반복문
    //사각형 갯수는  terrainSize - 1 *  terrainSize - 1
    //인덱스 갯수는  terrainSize - 1 *  terrainSize - 1 * 6
    for (int z = 0; z < Size - 1; z++)
    {
        for (int x = 0; x < Size - 1; x++)
        {
            //Vecindex.push_back(z* MaxZ + x);
            UINT Index = z * Size + x;
            //0
            Vecindex.push_back(Index);

            //1
            Vecindex.push_back(Index + 1);

            //2
            Vecindex.push_back(Index + Size + 1);

            //0
            Vecindex.push_back(Index);

            //1
            Vecindex.push_back(Index + Size + 1);

            //2
            Vecindex.push_back(Index + Size);
        }
    }
    indexCount = (UINT)Vecindex.size();
    indices = new UINT[indexCount];

    //벡터에 있던 내용 전체 복사

    copy(VecVertex.begin(), VecVertex.end(),
        stdext::checked_array_iterator<VertexTerrain*>(vertices, vertexCount));
    copy(Vecindex.begin(), Vecindex.end(),
        stdext::checked_array_iterator<UINT*>(indices, indexCount));

    SafeReset(mesh);
    mesh = make_shared<Mesh>(vertices, vertexCount, indices,
        indexCount, VertexType::TERRAIN);

    if (srv)
    {
        DeleteStructuredBuffer();
        CreateStructuredBuffer();
    }
}

void Terrain::UpdateMeshUv()
{
    VertexTerrain* vertices = (VertexTerrain*)mesh->vertices;
    int terrainSize = (int)sqrt(mesh->vertexCount);
    for (int z = 0; z < terrainSize; z++)
    {
        for (int x = 0; x < terrainSize; x++)
        {
            //배열에 들어갈 인덱스
            float _u = float(x) * uvScale / float(terrainSize - 1);
            float _v = float(z) * uvScale / float(terrainSize - 1);
            vertices[z * terrainSize + x].uv = Vector2(_u, _v);
        }
    }
    mesh->UpdateMesh();
}
void Terrain::CreateStructuredBuffer()
{
    //삼각형 단위
    inputArray = new InputDesc[triSize];
    for (UINT i = 0; i < triSize; i++)
    {
        UINT index0 = mesh->indices[i * 3 + 0];
        UINT index1 = mesh->indices[i * 3 + 1];
        UINT index2 = mesh->indices[i * 3 + 2];

        inputArray[i].v0 = ((VertexTerrain*)(mesh->vertices))[index0].position;
        inputArray[i].v1 = ((VertexTerrain*)(mesh->vertices))[index1].position;
        inputArray[i].v2 = ((VertexTerrain*)(mesh->vertices))[index2].position;

        inputArray[i].index = i;
    }
    //삼각형 단위
    outputArray = new OutputDesc[triSize];

    //input
    {
        ID3D11Buffer* buffer;
        D3D11_BUFFER_DESC desc = {};
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.ByteWidth = sizeof(InputDesc) * triSize;
        desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
        desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
        desc.StructureByteStride = sizeof(InputDesc);

        D3D11_SUBRESOURCE_DATA initData = {};
        initData.pSysMem = inputArray;

        D3D->GetDevice()->CreateBuffer(&desc, &initData, &buffer);
        input = (ID3D11Resource*)buffer;

        D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
        srvDesc.Format = DXGI_FORMAT_UNKNOWN;
        srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
        srvDesc.BufferEx.FirstElement = 0;
        srvDesc.BufferEx.NumElements = triSize;
        //srv
        D3D->GetDevice()->CreateShaderResourceView(buffer, &srvDesc, &srv);
    }
    //output
    {
        ID3D11Buffer* buffer;
        D3D11_BUFFER_DESC desc = {};
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.ByteWidth = sizeof(OutputDesc) * triSize;
        desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
        desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
        desc.StructureByteStride = sizeof(OutputDesc);

        D3D->GetDevice()->CreateBuffer(&desc, nullptr, &buffer);
        //ID3D11Resource
        output = (ID3D11Resource*)buffer;

        D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
        uavDesc.Format = DXGI_FORMAT_UNKNOWN;
        uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
        uavDesc.Buffer.FirstElement = 0;
        uavDesc.Buffer.NumElements = triSize;
        //uav
        D3D->GetDevice()->CreateUnorderedAccessView(buffer, &uavDesc, &uav);
    }
    //result read
    {
        ID3D11Buffer* buffer;
        D3D11_BUFFER_DESC desc = {};
        ((ID3D11Buffer*)output)->GetDesc(&desc);
        desc.Usage = D3D11_USAGE_STAGING;
        desc.BindFlags = 0;
        desc.MiscFlags = 0;
        desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;

        D3D->GetDevice()->CreateBuffer(&desc, nullptr, &buffer);

        result = (ID3D11Resource*)buffer;
    }
    
    {
        D3D11_BUFFER_DESC desc = { 0 };
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.ByteWidth = sizeof(RayDesc);
        desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;

        HRESULT hr = D3D->GetDevice()->CreateBuffer(&desc, NULL, &rayBuffer);
        assert(SUCCEEDED(hr));
    }
}

void Terrain::DeleteStructuredBuffer()
{
    input->Release();
    srv->Release();
    output->Release();
    uav->Release();
    result->Release();
    rayBuffer->Release();
    delete[] inputArray;
    delete[] outputArray;
}

bool Terrain::ComPutePicking(Ray WRay, OUT Vector3& HitPoint)
{
    //쉐이더부터 준비
    D3D->GetDC()->CSSetShader(computeShader, 0, 0);

    //raybuffer binding
    ray.position = WRay.position;
    ray.direction = WRay.direction;
    ray.size = (float)triSize;//삼각형갯수
    Matrix inverse = W.Invert();
    ray.direction = Vector3::TransformNormal(ray.direction, inverse);
    ray.direction.Normalize();
    ray.position = Vector3::Transform(ray.position, inverse);

    //트랜스폼한 Ray를 상수버퍼로 바인딩
    D3D->GetDC()->UpdateSubresource(rayBuffer, 0, NULL, &ray, 0, 0);

    D3D->GetDC()->CSSetConstantBuffers(0, 1, &rayBuffer);

    //input binding
    D3D->GetDC()->CSSetShaderResources(0, 1, &srv);
    //output binding
    D3D->GetDC()->CSSetUnorderedAccessViews(0, 1, &uav, nullptr);

    //멀티스레딩
    //shader 실행
    //올림
    UINT x = (UINT)ceil((float)triSize / 1024.0f);
    D3D->GetDC()->Dispatch(x, 1, 1);

    //동기화

    //gpu -> cpu 복사
    D3D->GetDC()->CopyResource(result, output);

    D3D11_MAPPED_SUBRESOURCE subResource;

    D3D->GetDC()->Map(result, 0, D3D11_MAP_READ, 0, &subResource);
    memcpy(outputArray, subResource.pData, sizeof(OutputDesc) * triSize);
    D3D->GetDC()->Unmap(result, 0);

    float minDistance = FLT_MAX;
    int minIndex = -1;
    for (int i = 0; i < triSize; i++)
    {
        OutputDesc temp = outputArray[i];
        if (temp.picked)
        {
            if (minDistance > temp.distance)
            {
                minDistance = temp.distance;
                minIndex = i;
            }
        }
    }
    
    if (minIndex >= 0)
    {
        HitPoint = ray.position + ray.direction
            * minDistance;
        HitPoint = Vector3::Transform(HitPoint, W);
        return true;
    }
    return false;
}

void Terrain::Render()
{
    Actor::Render();
    //다익스트라 노드 렌더링
}

완성된 터레인 클래스입니다. 헤더에 컴퓨트 셰이더가 static으로 들어간 이유는 다른 구조체 내에 있는 변수들에 의해 크기가 변경되면 안 되기 때문에 변화하지 않도록 설정했습니다. 2중 for문을 이용해서 삼각형 2개를 붙여 사각형을 만들었습니다. hlsl 파일에는 ambient, specular, diffuse가 추가되었습니다.

 

읽어주셔서 감사합니다.

'공부 > 3D강의' 카테고리의 다른 글

230718 강의  (0) 2023.07.18
230714 강의  (0) 2023.07.14
230712 강의  (0) 2023.07.12
230711 강의  (0) 2023.07.11
230710 강의  (0) 2023.07.10
Comments