0
\$\begingroup\$

This is a follow-up to this question.

The geometry shader is performing fantastically, but now I've lost the ability to display more than one tile type at once (the last one in the visible array is the only type showing up).

What am I missing in order to store the proper texture coordinates for every visible tile and not just the most recent?

Before/After screenshots at the end.

Reference code:

This function is called for every visible tile. For comparison, the dead code is what I was doing previously when the mesh was generated CPU-side.

  1. Calculate the texture coordinates for the tile (animation-related, but also used for static tiles) from the texture atlas.
  2. Calculate the world position of the bottom left corner of the tile.
  3. Convert the AABB texture atlas coordinates to GPU-friendly float2s.
  4. Add the vertex data to the dynamic mesh.
  5. Add the index data to the dynamic mesh.
  6. NEW: Update tile_cb constant buffer with the UVs.
void Tile::AddVertsForTile() const noexcept {
    const auto& sprite = _def->GetSprite();
    const auto& coords = sprite->GetCurrentTexCoords();

    const auto vert_left = _tile_coords.x + 0.0f;
    //const auto vert_right = _tile_coords.x + 1.0f;
    //const auto vert_top = _tile_coords.y + 0.0f;
    const auto vert_bottom = _tile_coords.y + 1.0f;

    const auto vert_bl = Vector2(vert_left, vert_bottom);
    //const auto vert_tl = Vector2(vert_left, vert_top);
    //const auto vert_tr = Vector2(vert_right, vert_top);
    //const auto vert_br = Vector2(vert_right, vert_bottom);

    const auto tx_left = coords.mins.x;
    const auto tx_right = coords.maxs.x;
    const auto tx_top = coords.mins.y;
    const auto tx_bottom = coords.maxs.y;

    const auto tx_bl = Vector2(tx_left, tx_bottom);
    const auto tx_tl = Vector2(tx_left, tx_top);
    const auto tx_tr = Vector2(tx_right, tx_top);
    const auto tx_br = Vector2(tx_right, tx_bottom);

    const float z = static_cast<float>(layer->z_index);
    const Rgba layer_color = layer->color;
    auto& builder = layer->GetMeshBuilder();

    const auto newColor = layer_color != color && color != Rgba::White ? color : layer_color;
    const auto normal = -Vector3::Z_AXIS;

    //builder.Begin(PrimitiveType::Triangles);
    builder.Begin(PrimitiveType::Points);
    builder.SetColor(newColor);
    builder.SetNormal(normal);

    builder.SetUV(tx_bl);
    builder.AddVertex(Vector3{vert_bl, z});
    
    //builder.SetUV(tx_tl);
    //builder.AddVertex(Vector3{vert_tl, z});

    //builder.SetUV(tx_tr);
    //builder.AddVertex(Vector3{vert_tr, z});

    //builder.SetUV(tx_br);
    //builder.AddVertex(Vector3{vert_br, z});

    //builder.AddIndicies(Mesh::Builder::Primitive::Quad);
    builder.AddIndicies(Mesh::Builder::Primitive::Point);

    //NEW: Update the GS cbuffer of texture coordinates
    auto cbuffer = sprite->GetMaterial()->GetShader()->GetConstantBuffers()[0];
    std::array<Vector2, 4> buffer{tx_bl, tx_tl, tx_tr, tx_br};
    cbuffer.get().Update(*g_theRenderer->GetDeviceContext(), buffer.data());

    builder.End(sprite->GetMaterial());

}

After the dynamic mesh is constructed it's rendered like so:

void Mesh::Render(Renderer& renderer, const Mesh::Builder& builder) noexcept {
    for(const auto& draw_inst : builder.draw_instructions) {
        renderer.SetMaterial(draw_inst.material);
        auto cbs = draw_inst.material->GetShader()->GetConstantBuffers();
        const auto cb_size = cbs.size();
        for(int i = 0; i < cb_size; ++i) {
            renderer.SetConstantBuffer(renderer.CONSTANT_BUFFER_START_INDEX + i, &(cbs.begin() + i)->get());
        }
        renderer.DrawIndexed(draw_inst.type, builder.verticies, builder.indicies, draw_inst.count, draw_inst.vertexStart, draw_inst.baseVertexLocation);
        for(int i = 0; i < cb_size; ++i) {
            renderer.SetConstantBuffer(renderer.CONSTANT_BUFFER_START_INDEX + i, nullptr);
        }
    }
}

Shader code:


cbuffer matrix_cb : register(b0) {
    float4x4 g_MODEL;
    float4x4 g_VIEW;
    float4x4 g_PROJECTION;
};

cbuffer time_cb : register(b1) {
    float g_GAME_TIME;
    float g_SYSTEM_TIME;
    float g_GAME_FRAME_TIME;
    float g_SYSTEM_FRAME_TIME;
}

cbuffer tile_cb : register(b3) {
    float2 tile_uv_bl;
    float2 tile_uv_tl;
    float2 tile_uv_tr;
    float2 tile_uv_br;
};

struct vs_in_t {
    float3 position : POSITION;
    float4 color : COLOR;
    float2 uv : UV;
};

struct gs_in_t {
    float4 position : POSITION;
    float4 color : COLOR;
    float2 uv : UV;
};

struct ps_in_t {
    float4 position : SV_POSITION;
    float4 color : COLOR;
    float2 uv : UV;
};

SamplerState sSampler : register(s0);

Texture2D<float4> tDiffuse    : register(t0);
Texture2D<float4> tNormal   : register(t1);
Texture2D<float4> tDisplacement : register(t2);
Texture2D<float4> tSpecular : register(t3);
Texture2D<float4> tOcclusion : register(t4);
Texture2D<float4> tEmissive : register(t5);

gs_in_t VertexFunction(vs_in_t input_vertex) {
    gs_in_t output = (gs_in_t)0;

    float4 local = float4(input_vertex.position, 1.0f);
    float4 world = mul(local, g_MODEL);

    output.position = world;
    output.color = input_vertex.color;
    output.uv = input_vertex.uv;

    return output;
}

[maxvertexcount(4)]
void GeometryFunction(point gs_in_t input[1], inout TriangleStream<ps_in_t> stream) {
    ps_in_t output = (ps_in_t)0;

    float px = input[0].position.x;
    float py = input[0].position.y;
    //TODO: Have fun with the world positions here...
    float2 new_position = float2(px, py);
    float4 world = float4(new_position, 0.0f, 1.0f);

    //Top Left
    output.position = mul(mul(float4(world.xy + float2(0.0f, 1.0f), 0.0f, 1.0f), g_VIEW), g_PROJECTION);
    output.color = input[0].color;
    output.uv = tile_uv_tl;
    stream.Append(output);

    //Bottom Left
    output.position = mul(mul(float4(world.xy + float2(0.0f, 0.0f), 0.0f, 1.0f), g_VIEW), g_PROJECTION);
    output.color = input[0].color;
    output.uv = tile_uv_bl;
    stream.Append(output);

    //Top Right
    output.position = mul(mul(float4(world.xy + float2(1.0f, 1.0f), 0.0f, 1.0f), g_VIEW), g_PROJECTION);
    output.color = input[0].color;
    output.uv = tile_uv_tr;
    stream.Append(output);
    
    //Bottom Right
    output.position = mul(mul(float4(world.xy + float2(1.0f, 0.0f), 0.0f, 1.0f), g_VIEW), g_PROJECTION);
    output.color = input[0].color;
    output.uv = tile_uv_br;
    stream.Append(output);

    stream.RestartStrip();
}

float4 PixelFunction(ps_in_t input_pixel) : SV_Target0 {
    float4 albedo = tDiffuse.Sample(sSampler, input_pixel.uv);
    float4 final_color = albedo * input_pixel.color;
    clip(final_color.a - 0.1);
    return final_color;
}

Before implementing the geometry shader, CPU-side tile mesh generation:

CPU-side tile mesh generation

After implementing the geometry shader, GPU-side tile mesh generation:

GPU-side tile mesh generation

\$\endgroup\$
2
  • 1
    \$\begingroup\$ In the past for solving problems like this, I've stored tile IDs in a texture. \$\endgroup\$
    – DMGregory
    Dec 4, 2020 at 13:20
  • \$\begingroup\$ @DMGregory I can't believe I forgot that was an option. I even gave this advice to someone a couple years ago...Time to brush up on UAVs and dynamic staging textures. :) \$\endgroup\$
    – Casey
    Dec 4, 2020 at 15:27

0

You must log in to answer this question.

Browse other questions tagged .