Une classe Skybox pour afficher un ciel autour de votre caméra – 1ère approche

skybox

Intro :

Pour que votre scène soit réaliste, il peut être utile d’afficher un décor de ciel de grande taille autour de la caméra.

Ceci constitue une première approche à l’affichage d’une Skybox.

Prérequis :

– Savoir utiliser la classe ShaderTechnique. Voir cet article.

– Savoir utiliser la classe MeshSceneNode. Voir cet article.

– Savoir un peu utiliser DirectX 10.

Explications :

Tout d’abord la classe dérivant ShaderTechnique :

//------------------------------------------------------
// Une simple effet affichant une SkyBox
//------------------------------------------------------
class ShaderTechnique_SkyBox : public ShaderTechnique
{
public:
    friend class SkyBoxSceneNode;

    ShaderTechnique_SkyBox();
    virtual ~ShaderTechnique_SkyBox();

    virtual bool Initialize();

    virtual void SetupShaderVariables();

    virtual void Update(float fTimeSinceLastFrame);
};

 

Voici ensuite le fichier ShaderTechnique_SkyBox.cpp :

ShaderTechnique_SkyBox::ShaderTechnique_SkyBox() :
ShaderTechnique("SkyBox.fx", "Render", VertexLayoutType::PTN_VERTEX)
{
}

ShaderTechnique_SkyBox::~ShaderTechnique_SkyBox()
{
}

bool ShaderTechnique_SkyBox::Initialize()
{
    ShaderTechnique::Initialize();

    SetupShaderVariables();

    return true;
}

void ShaderTechnique_SkyBox::SetupShaderVariables()
{
    RegisterMatrixVariable("World", ShaderVariableType::WORLD);
    RegisterMatrixVariable("View", ShaderVariableType::VIEW);
    RegisterMatrixVariable("Projection", ShaderVariableType::PROJECTION);

    RegisterTextureVariable("txFace");
}

void ShaderTechnique_SkyBox::Update(float fTimeSinceLastFrame)
{
    SetAutoMatrix(ShaderVariableType::WORLD);
    SetAutoMatrix(ShaderVariableType::VIEW);
    SetAutoMatrix(ShaderVariableType::PROJECTION);
}

 

Voici le fichier SkyBoxSceneNode.h :

#ifndef SKY_BOX_SCENE_NODE_H
#define SKY_BOX_SCENE_NODE_H

class SkyBoxSceneNode : public MeshSceneNode
{
public:
    SkyBoxSceneNode(ID3D10ShaderResourceView* pTop, ID3D10ShaderResourceView* pBottom,
        ID3D10ShaderResourceView* pLeft, ID3D10ShaderResourceView* pRight,
        ID3D10ShaderResourceView* pFront, ID3D10ShaderResourceView* pBack);

    virtual ~SkyBoxSceneNode();

    virtual bool Initialize();
    virtual void Render(float fTimeSinceLastFrame);

protected:
    virtual void OnPreRender(float fTimeSinceLastFrame);
    virtual void OnPostRender(float fTimeSinceLastFrame);

private:
    PTNVertex SetVertex(D3DXVECTOR3 p, D3DXVECTOR2 t, D3DXVECTOR3 n);

private:
    ID3D10ShaderResourceView* m_pFrontTex;
    ID3D10ShaderResourceView* m_pLeftTex;
    ID3D10ShaderResourceView* m_pBackTex;
    ID3D10ShaderResourceView* m_pRightTex;
    ID3D10ShaderResourceView* m_pTopTex;
    ID3D10ShaderResourceView* m_pBottomTex;

    ID3D10Buffer* m_pFrontVB;
    ID3D10Buffer* m_pLeftVB;
    ID3D10Buffer* m_pBackVB;
    ID3D10Buffer* m_pRightVB;
    ID3D10Buffer* m_pTopVB;
    ID3D10Buffer* m_pBottomVB;
};

#endif

 

Voici le fichier SkyBoxSceneNode.cpp :

#include "SkyBoxSceneNode.h"

SkyBoxSceneNode::SkyBoxSceneNode(ID3D10ShaderResourceView* pTop, ID3D10ShaderResourceView* pBottom,
        ID3D10ShaderResourceView* pLeft, ID3D10ShaderResourceView* pRight,
        ID3D10ShaderResourceView* pFront, ID3D10ShaderResourceView* pBack) :
m_pFrontVB(nullptr),
m_pLeftVB(nullptr),
m_pBackVB(nullptr),
m_pRightVB(nullptr),
m_pTopVB(nullptr),
m_pBottomVB(nullptr),
m_pFrontTex(pFront),
m_pLeftTex(pLeft),
m_pBackTex(pBack),
m_pRightTex(pRight),
m_pTopTex(pTop),
m_pBottomTex(pBottom)
{
    SetVertexType(VertexLayoutType::PTN_VERTEX);
    SetDrawMethod(DrawMethod::DRAW_INDEXED);
    SetTopology(D3D_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    SetShaderTechnique( SHADER_MANAGER->GetShader("SkyBox") );
}

SkyBoxSceneNode::~SkyBoxSceneNode()
{
}

PTNVertex SkyBoxSceneNode::SetVertex(D3DXVECTOR3 p, D3DXVECTOR2 t, D3DXVECTOR3 n)
{
    PTNVertex vertex;
    
    vertex.position = p;
    vertex.texture = t;
    vertex.normal = n;

    return vertex;
}

bool SkyBoxSceneNode::Initialize()
{
    std::vector<unsigned short> indices;

    indices.push_back(3);
    indices.push_back(1);
    indices.push_back(0);
    indices.push_back(2);
    indices.push_back(1);
    indices.push_back(3);

    BuildIB(indices);

    // On redecoupe les bords des textures pour éviter
    // d'afficher les arrêtes du cube
    float fTextureWidth = GetShaderTextureViewSize(m_pBottomTex).x;

    float onepixel = 1.0f / (fTextureWidth * 1.5f);
    float t = 1.0f - onepixel;
    float o = 0.0f + onepixel;

    std::vector<PTNVertex> vertices;

    // Front side
    vertices.push_back( SetVertex(D3DXVECTOR3(-1,-1,-1), D3DXVECTOR2(t, t), D3DXVECTOR3(0, 0, 1.0f)));
    vertices.push_back( SetVertex(D3DXVECTOR3(1,-1,-1),  D3DXVECTOR2(o, t), D3DXVECTOR3(0, 0, 1.0f)));
    vertices.push_back( SetVertex(D3DXVECTOR3(1, 1,-1),  D3DXVECTOR2(o, o), D3DXVECTOR3(0, 0, 1.0f)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1, 1,-1), D3DXVECTOR2(t, o), D3DXVECTOR3(0, 0, 1.0f)));

    BuildVB(vertices);

    m_pFrontVB = GetVertexBuffer();

    // Left side
    vertices.clear();

    vertices.push_back( SetVertex(D3DXVECTOR3(1,-1,-1), D3DXVECTOR2(t, t), D3DXVECTOR3(-1, 0, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(1,-1, 1), D3DXVECTOR2(o, t), D3DXVECTOR3(-1, 0, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(1, 1, 1), D3DXVECTOR2(o, o), D3DXVECTOR3(-1, 0, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(1, 1,-1), D3DXVECTOR2(t, o), D3DXVECTOR3(-1, 0, 0)));

    BuildVB(vertices);

    m_pLeftVB = GetVertexBuffer();

    // Back side
    vertices.clear();

    vertices.push_back( SetVertex(D3DXVECTOR3( 1,-1, 1), D3DXVECTOR2(t, t), D3DXVECTOR3(0, 0, -1.0f)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1,-1, 1), D3DXVECTOR2(o, t), D3DXVECTOR3(0, 0, -1.0f)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1, 1, 1), D3DXVECTOR2(o, o), D3DXVECTOR3(0, 0, -1.0f)));
    vertices.push_back( SetVertex(D3DXVECTOR3( 1, 1, 1), D3DXVECTOR2(t, o), D3DXVECTOR3(0, 0, -1.0f)));

    BuildVB(vertices);

    m_pBackVB = GetVertexBuffer();

    // Right side
    vertices.clear();

    vertices.push_back( SetVertex(D3DXVECTOR3(-1,-1, 1), D3DXVECTOR2(t, t), D3DXVECTOR3(1, 0, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1,-1,-1), D3DXVECTOR2(o, t), D3DXVECTOR3(1, 0, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1, 1,-1), D3DXVECTOR2(o, o), D3DXVECTOR3(1, 0, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1, 1, 1), D3DXVECTOR2(t, o), D3DXVECTOR3(1, 0, 0)));

    BuildVB(vertices);

    m_pRightVB = GetVertexBuffer();

    // Top side
    vertices.clear();

    vertices.push_back( SetVertex(D3DXVECTOR3( 1, 1,-1), D3DXVECTOR2(t, t), D3DXVECTOR3(0, -1, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3( 1, 1, 1), D3DXVECTOR2(o, t), D3DXVECTOR3(0, -1, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1, 1, 1), D3DXVECTOR2(o, o), D3DXVECTOR3(0, -1, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1, 1,-1), D3DXVECTOR2(t, o), D3DXVECTOR3(0, -1, 0)));

    BuildVB(vertices);

    m_pTopVB = GetVertexBuffer();

    // Bottom side
    vertices.clear();

    vertices.push_back( SetVertex(D3DXVECTOR3( 1,-1, 1), D3DXVECTOR2(t, t), D3DXVECTOR3(0, 1, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3( 1,-1,-1), D3DXVECTOR2(o, t), D3DXVECTOR3(0, 1, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1,-1,-1), D3DXVECTOR2(o, o), D3DXVECTOR3(0, 1, 0)));
    vertices.push_back( SetVertex(D3DXVECTOR3(-1,-1, 1), D3DXVECTOR2(t, o), D3DXVECTOR3(0, 1, 0)));

    BuildVB(vertices);

    m_pBottomVB = GetVertexBuffer();

    return true;
}

void SkyBoxSceneNode::Render(float fTimeSinceLastFrame)
{
    D3D10_RENDERER->EnableZBuffer(false);

    SetVertexBuffer(m_pFrontVB);
    GetShaderTechnique()->SetTextureRV("txFace", m_pFrontTex);
    Renderable::Render(fTimeSinceLastFrame);

    SetVertexBuffer(m_pLeftVB);
    GetShaderTechnique()->SetTextureRV("txFace", m_pLeftTex);
    Renderable::Render(fTimeSinceLastFrame);

    SetVertexBuffer(m_pBackVB);
    GetShaderTechnique()->SetTextureRV("txFace", m_pBackTex);
    Renderable::Render(fTimeSinceLastFrame);

    SetVertexBuffer(m_pRightVB);
    GetShaderTechnique()->SetTextureRV("txFace", m_pRightTex);
    Renderable::Render(fTimeSinceLastFrame);

    SetVertexBuffer(m_pTopVB);
    GetShaderTechnique()->SetTextureRV("txFace", m_pTopTex);
    Renderable::Render(fTimeSinceLastFrame);

    SetVertexBuffer(m_pBottomVB);
    GetShaderTechnique()->SetTextureRV("txFace", m_pBottomTex);
    Renderable::Render(fTimeSinceLastFrame);

    D3D10_RENDERER->EnableZBuffer(true);
}

void SkyBoxSceneNode::OnPreRender(float fTimeSinceLastFrame)
{
    Camera* pCamera = SCENE_MANAGER->GetActiveCamera();

    D3DXMATRIX finalMatrix;

    D3DXMATRIX translate;

    D3DXVECTOR3 cameraPos = pCamera->GetPosition();

    // On centre toujours la skybox autour de la position de la caméra
    D3DXMatrixTranslation(&translate, cameraPos.x, cameraPos.y, cameraPos.z);

    float viewDistance = (pCamera->GerNearValue() + pCamera->GetFarValue()) * 0.5f;

    // On agrandie la skybox pour qu'elle est une bonne taille
    D3DXMATRIX scale;
    D3DXMatrixScaling(&scale, viewDistance, viewDistance, viewDistance);

    finalMatrix = scale * translate;

    SCENE_MANAGER->PushAndSetMatrix(finalMatrix);
}

void SkyBoxSceneNode::OnPostRender(float fTimeSinceLastFrame)
{    
    SCENE_MANAGER->PopMatrix();
}

 

Voici le fichier SkyBox.fx :

matrix World;
matrix View;
matrix Projection;

Texture2D txFace;

SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

struct VS_INPUT
{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD;
    float3 Norm : NORMAL;
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD0;
    float3 Norm : NORMAL;
};

PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );

    output.Tex = input.Tex;
    
    output.Norm = mul( input.Norm, World );
    
    return output;
}

float4 PS( PS_INPUT input) : SV_Target
{
    float4 textureColor = txFace.Sample(samLinear, input.Tex);
    
    return textureColor;
}

technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

 

Résumé :

Nous avons présenté un énorme cube autour de la caméra qui servira de ciel ou de décor.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *