Skip to content

Latest commit

 

History

History

06-CubeMapArray

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

Cube Map Array Tutorial

Windows (DirectX 12)       
Linux (Vulkan)             
MacOS (Metal)              
iOS (Metal)
CubeMapArray on Windows CubeMapArray on Linux CubeMapArray on MacOS CubeMapArray on iOS * No simulator support

This tutorial demonstrates cube-map array texturing and sky-box rendering with Methane Kit:

Tutorial demonstrates the following techniques:

  • Loading face images to cube map texture and using it for sky-box rendering in the background;
  • Creating a cube-map array render target texture;
  • Rendering text labels to the faces of the cube-map array texture via separate render passes using the helper class TextureLabeler;
  • Instanced rendering of multiple cubes displaying all faces of the pre-rendered cube-map array texture;
  • Using the Sky-box rendering extension with a panoramic cube-map texture loaded from image files.

Application Controls

Common keyboard controls are enabled by the Platform, Graphics and UserInterface application controllers:

Cube-Map Texture Initialization

Cube-map texture is created using settings generated with rhi::ITexture::Settings::ForCubeImage(...) and is set to the TexturedMeshBuffers instance with m_cube_buffers_ptr->SetTexture(...). Then this texture is bound to the Pixel shader argument g_texture_array in frame.cube.program_bindings initialization.

The content of the texture is rendered to its faces using the TextureLabeler class, which renders face identification text labels to the texture faces. Rendering is done after the UserInterfaceApp::CompleteInitialization() call to ensure that all necessary resources have been uploaded to the GPU.

void CubeMapArrayApp::Init()
{
    ...
    
    // Create cube mesh buffer resources
    gfx::CubeMesh<CubeVertex> cube_mesh(CubeVertex::layout);
    m_cube_buffers_ptr = std::make_unique<TexturedMeshBuffers>(render_cmd_queue, std::move(cube_mesh), "Cube");

    // Create cube-map render target texture
    m_cube_buffers_ptr->SetTexture(
        rhi::Texture(
            GetRenderContext(),
            rhi::ITexture::Settings::ForCubeImage(
                g_cube_texture_size, CUBE_MAP_ARRAY_SIZE, gfx::PixelFormat::RGBA8Unorm, false,
                rhi::ResourceUsageMask({ rhi::ResourceUsage::RenderTarget, rhi::ResourceUsage::ShaderRead })
            )));
            
    ...
    
    // Create frame buffer resources
    for(CubeMapArrayFrame& frame : GetFrames())
    {
        // Configure program resource bindings
        frame.cube.program_bindings = m_render_state.GetProgram().CreateBindings({
            { { rhi::ShaderType::Pixel,  "g_texture_array" }, m_cube_buffers_ptr->GetTexture().GetResourceView() },
            { { rhi::ShaderType::Pixel,  "g_sampler"       }, m_texture_sampler.GetResourceView()                },
        }, frame.index);
        frame.cube.uniforms_argument_binding_ptr = &frame.cube.program_bindings.Get({ rhi::ShaderType::Vertex, "g_uniforms" });
        ...
    }
    
    // Create all resources for texture labels rendering before resources upload in UserInterfaceApp::CompleteInitialization()
    TextureLabeler cube_texture_labeler(GetUIContext(), GetFontContext(), m_cube_buffers_ptr->GetTexture(),
                                        rhi::ResourceState::Undefined, { g_cube_texture_size / 4U, 10U });

    // Upload all resources, including font texture and text mesh buffers required for rendering
    UserInterfaceApp::CompleteInitialization();
    
    // Encode and execute texture labels rendering commands when all resources are uploaded and ready on GPU
    cube_texture_labeler.Render();

    GetRenderContext().WaitForGpu(rhi::IContext::WaitFor::RenderComplete);
}

Cube-Map Rendering

Rendering of the cube instances is done similarly to other tutorials, but with the use of the instance_count parameter in the m_cube_buffers_ptr->Draw(..., CUBE_MAP_ARRAY_SIZE) call. The instance_id related to this count is passed in the vertex shader and used to form texture coordinates for cube-map array texture sampling.

bool CubeMapArrayApp::Render()
{
    ...
    
    META_DEBUG_GROUP_VAR(s_debug_group, "Cube Instances Rendering");
    frame.render_cmd_list.ResetWithState(m_render_state, &s_debug_group);
    frame.render_cmd_list.SetViewState(GetViewState());
    m_cube_buffers_ptr->Draw(frame.render_cmd_list, frame.cube.program_bindings, 0U, CUBE_MAP_ARRAY_SIZE);
    
    ...
}

Cube-Map Array Shaders

CubeMapArrayUniforms.h

Uniforms structure with array of MVP matrices for all cube instances:

```cpp
#define CUBE_MAP_ARRAY_SIZE 8 // NOSONAR

struct Uniforms
{
    float4x4 mvp_matrix_per_instance[CUBE_MAP_ARRAY_SIZE]; // NOSONAR
};

CubeMapArray.hlsl

Vertex shader prepares texture coordinates uvwi for cube-map texture sampling by writing the cube vertex position to uvw texture coordinates and the instance id to the i component, which is used as an index in the texture array. Sampling of the cube-map array texture g_texture_array is done in the pixel shader from interpolated coordinates uvwi.

```cpp
#include "CubeMapArrayUniforms.h"

struct VSInput
{
    uint   instance_id : SV_InstanceID;
    float3 position    : POSITION;
};

struct PSInput
{
    float4 position : SV_POSITION;
    float4 uvwi     : UVFACE;
};

ConstantBuffer<Uniforms>  g_uniforms      : register(b0, META_ARG_FRAME_CONSTANT);
TextureCubeArray          g_texture_array : register(t0, META_ARG_CONSTANT);
SamplerState              g_sampler       : register(s0, META_ARG_CONSTANT);

PSInput CubeVS(VSInput input)
{
    PSInput output;
    output.position = mul(float4(input.position, 1.F), g_uniforms.mvp_matrix_per_instance[input.instance_id]);
    output.uvwi     = float4(-input.position, input.instance_id); // use position with negative sign to fix texture reflection
    return output;
}

float4 CubePS(PSInput input) : SV_TARGET
{
    return g_texture_array.Sample(g_sampler, input.uvwi);
}

Continue learning

Continue learning Methane Graphics programming in the next tutorial ParallelRendering, which is demonstrating multi-threaded rendering with IParallelRenderCommandList.