Maverick Project
 Microsoft Visual C++ 2012 Express
 Direct3D 11.0
 Kinect for Windows SDK v1.7
 DirectXTex texture processing library

■Kinect スケルトンの検出 Prev Top Next
関連ページ:Direct3D 自作の共通ライブラリ


ジョイントをレンダリング

今回はスケルトンを検出して、ジョイントをレンダリングします。

UCommon.h 共通で使用するヘッダファイル
UException.h 例外処理クラスのヘッダファイル
UDirect3D11.h Direct3Dクラスのヘッダーファイル
UDirect3D11.cpp Direct3Dクラスのソースファイル
UGraphicsPipeline.h グラフィックパイプラインクラスのヘッダーファイル
UGraphicsPipeline.cpp グラフィックパイプラインクラスのソースファイル
UMesh.h メッシュの抽象クラスのヘッダーファイル
UMesh.cpp メッシュの抽象クラスのソースファイル
UDDSLoader.h DDSファイルをロードしてシェーダーリソースビューを作成するサンプルクラスのヘッダファイル。
R8G8B8A8またはR8G8B8X8のみサポートし、ミップマップ、キューブマップ、ボリュームマップは未対応。
UDDSLoader.cpp DDSファイルをロードしてシェーダーリソースビューを作成するサンプルクラスのソースファイル。
UPlane04_0.h RGBカメラから取得したイメージをレンダリングするためのメッシュクラスのヘッダーファイル
UPlane04_0.cpp RGBカメラから取得したイメージをレンダリングするためのメッシュクラスのソースファイル
UPlane04_1.h ジョイントをレンダリングするためのメッシュクラスのヘッダーファイル
UPlane04_1.cpp ジョイントをレンダリングするためのメッシュクラスのソースファイル
Sample04.hlsl hlslファイル
UKinect04.h Kinectクラスのヘッダーファイル
UKinect04.cpp Kinectクラスのソースファイル
main.cpp main関数のソースファイル

---UPlane04_0.h---  ↑


#ifndef UPLANE04_0_H
#define UPLANE04_0_H

#include "../../Common/Header/UCommon.h"
#include "../../Common/Header/UException.h."
#include "../../Common/Header/UMesh.h"
#include "../../Common/Header/UDirect3D11.h"
#include <NuiApi.h>

class UCPlane04_0: public UIMesh
{
private:
   // 頂点定義
   typedef struct _VERTEX
   {
      // 頂点座標
      float x, y, z;
      // テクセル
      float tu, tv;
   }VERTEX;

   // Direct3D用定数バッファ設定用構造体
   typedef struct _CONSTANT_BUFFER
   {
      XMFLOAT4 Position;
      XMFLOAT4 Color;

      _CONSTANT_BUFFER(){};
      _CONSTANT_BUFFER( XMFLOAT4 position, XMFLOAT4 color )
      {
         ::CopyMemory( &Position, &position, sizeof( XMFLOAT4 ) );
         ::CopyMemory( &Color, &color, sizeof( XMFLOAT4 ) );
      };
   }CONSTANT_BUFFER;

   // 頂点バッファ
   ID3D11Buffer* m_pVertexBuffer;

   // インデックスバッファ
   ID3D11Buffer* m_pIndexBuffer;

   // シェーダーリソースビュー
   ID3D11ShaderResourceView* m_pSRView;

   // サンプラーステート
   ID3D11SamplerState* m_pSamplerState;

   // 定数バッファ
   ID3D11Buffer* m_pConstantBuffers;

public:
   UCPlane04_0();
   ~UCPlane04_0();
   void Invalidate();
   void CreateMesh( ID3D11Device* pD3DDevice, UINT Width, UINT Height, ID3D11Buffer** ppConstantBuffers );
   void NextFrame( ID3D11DeviceContext* pD3DDeviceContext, NUI_LOCKED_RECT* pRect );
   void Render( ID3D11DeviceContext* pD3DDeviceContext );
};

#endif

---UPlane04_0.cpp---  ↑

#include "UPlane04_0.h"

UCPlane04_0::UCPlane04_0()
{
   m_pVertexBuffer = nullptr;
   m_pIndexBuffer = nullptr;
   m_pSRView = nullptr;
   m_pSamplerState = nullptr;
}

UCPlane04_0::~UCPlane04_0()
{
   Invalidate();
}

void UCPlane04_0::Invalidate()
{
   SAFE_RELEASE( m_pSamplerState );
   SAFE_RELEASE( m_pSRView );
   SAFE_RELEASE( m_pIndexBuffer );
   SAFE_RELEASE( m_pVertexBuffer );
}

void UCPlane04_0::CreateMesh( ID3D11Device* pD3DDevice, UINT Width, UINT Height, ID3D11Buffer** ppConstantBuffers )
{
   // 頂点データを格納する
   VERTEX v[] = { 
       1,  1, 0.0f, 1.0f, 0.0f,
      -1,  1, 0.0f, 0.0f, 0.0f,
       1, -1, 0.0f, 1.0f, 1.0f,
      -1, -1, 0.0f, 0.0f, 1.0f,
   };
      
   // 頂点バッファを作成する
   m_pVertexBuffer = CreateVertexBuffer( pD3DDevice, v, sizeof( v ), 0 );

   UINT Index[] = { 0, 1, 2, 3 };

   // インデックスバッファを作成する。
   m_pIndexBuffer = CreateIndexBuffer( pD3DDevice, Index, sizeof( Index ), 0 );

   // 空のデカールマップ用シェーダーリソースビューを作成する
   // GPU (読み取りのみ) と CPU (書き込みのみ) によるアクセスが可能なリソースとして作成する設定
   m_pSRView = CreateSRViewForDecalMap( pD3DDevice, Width, Height, UD3D11_FORMAT, UEACCESS_FLAG::UPDATE_SUBRESOURCE );

   // サンプラーステートを作成する
   m_pSamplerState = CreateSamplerState( pD3DDevice, D3D11_TEXTURE_ADDRESS_CLAMP );
   
   m_pConstantBuffers = *ppConstantBuffers;
}

void UCPlane04_0::NextFrame( ID3D11DeviceContext* pD3DDeviceContext, NUI_LOCKED_RECT* pRect )
{
   ID3D11Resource* pResource = nullptr;

   __try
   {
      // シェーダーリソースビューからテクスチャーを取得する
      m_pSRView->GetResource( &pResource );

      // シェーダーリソースビューに色情報を設定する
      pD3DDeviceContext->UpdateSubresource( pResource, 0, nullptr, pRect->pBits, pRect->Pitch, 0 );
   }
   __finally
   {
      SAFE_RELEASE( pResource );
   }
}

void UCPlane04_0::Render( ID3D11DeviceContext* pD3DDeviceContext )
{
   HRESULT hr = E_FAIL;

   // 頂点バッファ設定
   UINT stride = sizeof( UCPlane04_0::VERTEX );
   UINT offset = 0;
   pD3DDeviceContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &stride, &offset );

   // インデックスバッファ設定
   pD3DDeviceContext->IASetIndexBuffer( m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );

   // プリミティブ タイプおよびデータの順序に関する情報を設定
   pD3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

   // ピクセルシェーダーのサンプラーステートを設定する
   pD3DDeviceContext->PSSetSamplers( 0, 1, &m_pSamplerState );

   // デカールマップを設定する
   pD3DDeviceContext->PSSetShaderResources( 0, 1, &m_pSRView );

   D3D11_MAPPED_SUBRESOURCE mappedResource;
   if( FAILED( hr = pD3DDeviceContext->Map( m_pConstantBuffers, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource ) ) )
      throw( UCException( -1, _T("UCPlane04_0::Render()でエラーが発生しました。Map()が失敗しました。") ) );

   CONSTANT_BUFFER* cbuffer = (CONSTANT_BUFFER*)(mappedResource.pData);
   ::CopyMemory( &cbuffer->Position, &XMFLOAT4( 0, 0, 0, 0 ), sizeof( XMFLOAT4 ));
   ::CopyMemory( &cbuffer->Color, &XMFLOAT4( 1, 1, 1, 1 ), sizeof( XMFLOAT4 ));
   pD3DDeviceContext->Unmap( m_pConstantBuffers, 0 );

   // 定数バッファを設定
   pD3DDeviceContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffers );
   pD3DDeviceContext->PSSetConstantBuffers( 0, 1, &m_pConstantBuffers );

   // インデックスバッファを使用した描画
    pD3DDeviceContext->DrawIndexed( 4, 0, 0 );
}

RGBカメラから取得したイメージをレンダリングするためのメッシュクラスです。

---UPlane04_1.h---  ↑

#ifndef UPLANE04_1_H
#define UPLANE04_1_H

#include "../../Common/Header/UCommon.h"
#include "../../Common/Header/UException.h."
#include "../../Common/Header/UMesh.h"
#include "../../Common/Header/UDDSLoader.h"
#include "../../Common/Header/UDirect3D11.h"
#include <NuiApi.h>

class UCPlane04_1: public UIMesh
{
private:
   // 頂点定義
   typedef struct _VERTEX
   {
      // 頂点座標
      float x, y, z;
      // テクセル
      float tu, tv;
   }VERTEX;

   // Direct3D用定数バッファ設定用構造体
   typedef struct _CONSTANT_BUFFER
   {
      XMFLOAT4 Position;
      XMFLOAT4 Color;

      _CONSTANT_BUFFER(){};
      _CONSTANT_BUFFER( XMFLOAT4 position, XMFLOAT4 color )
      {
         ::CopyMemory( &Position, &position, sizeof( XMFLOAT4 ) );
         ::CopyMemory( &Color, &color, sizeof( XMFLOAT4 ) );
      };
   }CONSTANT_BUFFER;

   // 頂点バッファ
   ID3D11Buffer* m_pVertexBuffer;

   // インデックスバッファ
   ID3D11Buffer* m_pIndexBuffer;

   // シェーダーリソースビュー
   ID3D11ShaderResourceView* m_pSRView;

   // サンプラーステート
   ID3D11SamplerState* m_pSamplerState;

   UINT m_SkeletonFrameCount;

   // ジョイント座標
   CONSTANT_BUFFER* m_SkeletonFrameArray;

   // スケルトン座標をスクリーン座標系に変換する
   void TransformSkeletonToScreenCoordinate( Vector4* pSkeletonPosition,
                                             NUI_IMAGE_RESOLUTION imageFrameResolution, NUI_IMAGE_RESOLUTION depthFrameResolution,
                                             LONG* pColorX, LONG* pColorY );

   // スクリーン座標系の座標を射影座標系の座標に変換する
   inline float TransformScreenToProjectionCoordinate( float ScreenSize, float ScreenPos )
   {
      return ScreenPos / ScreenSize * 2.0f - 1.0f;
   }

   // 定数バッファ
   ID3D11Buffer* m_pConstantBuffers;

public:
   UCPlane04_1();
   ~UCPlane04_1();
   void Invalidate();
   void CreateMesh( ID3D11Device* pD3DDevice, float Width, float Height, TCHAR* pTextureFileName, UINT SkeletonFrameCount, ID3D11Buffer** ppConstantBuffers );
   // Kinectから取得したスケルトンをDirect3D用に変換する
   void NextFrame( NUI_SKELETON_FRAME* pSrvSkeleton,
                   NUI_IMAGE_RESOLUTION imageFrameResolution, NUI_IMAGE_RESOLUTION depthFrameResolution,
                   float Width, float Height );
   void Render( ID3D11DeviceContext* pD3DDeviceContext );
};

#endif

---UPlane04_1.cpp---  ↑

#include "UPlane04_1.h"

UCPlane04_1::UCPlane04_1()
{
   m_pVertexBuffer = nullptr;
   m_pIndexBuffer = nullptr;
   m_pSRView = nullptr;
   m_pSamplerState = nullptr;
   m_SkeletonFrameCount = 0;
   m_SkeletonFrameArray = nullptr;
}

UCPlane04_1::~UCPlane04_1()
{
   Invalidate();
}

void UCPlane04_1::Invalidate()
{
   SAFE_DELETE_ARRAY( m_SkeletonFrameArray );
   SAFE_RELEASE( m_pSamplerState );
   SAFE_RELEASE( m_pSRView );
   SAFE_RELEASE( m_pIndexBuffer );
   SAFE_RELEASE( m_pVertexBuffer );
}

void UCPlane04_1::CreateMesh( ID3D11Device* pD3DDevice, float Width, float Height, TCHAR* pTextureFileName, UINT SkeletonFrameCount, ID3D11Buffer** ppConstantBuffers )
{
   // 頂点データを格納する
   VERTEX v[] = { 
       Width,  Height, 0.0f, 1.0f, 0.0f,
      -Width,  Height, 0.0f, 0.0f, 0.0f,
       Width, -Height, 0.0f, 1.0f, 1.0f,
      -Width, -Height, 0.0f, 0.0f, 1.0f,
   };

   // 頂点バッファを作成する
   m_pVertexBuffer = CreateVertexBuffer( pD3DDevice, v, sizeof( v ), 0 );

   UINT Index[] = { 0, 1, 2, 3 };

   // インデックスバッファを作成する。
   m_pIndexBuffer = CreateIndexBuffer( pD3DDevice, Index, sizeof( Index ), 0 );

   // デカールマップをロード
   CreateDDSTextureFromFile( pD3DDevice, pTextureFileName, &m_pSRView );

   // サンプラーステートを作成する
   m_pSamplerState = CreateSamplerState( pD3DDevice, D3D11_TEXTURE_ADDRESS_CLAMP );

   m_SkeletonFrameCount = SkeletonFrameCount;
   m_SkeletonFrameArray = NEW CONSTANT_BUFFER[m_SkeletonFrameCount];
   
   m_pConstantBuffers = *ppConstantBuffers;
}

// スケルトン座標をスクリーン座標系に変換する
void UCPlane04_1::TransformSkeletonToScreenCoordinate( Vector4* pSkeletonPosition,
                                                       NUI_IMAGE_RESOLUTION imageFrameResolution, NUI_IMAGE_RESOLUTION depthFrameResolution,
                                                       LONG* pColorX, LONG* pColorY )
{
   HRESULT hr = E_FAIL;
   FLOAT depthX = 0, depthY = 0;
   *pColorX = *pColorY = 0;

   // スケルトンの座標を深度イメージの座標系に変換する
   ::NuiTransformSkeletonToDepthImage( *pSkeletonPosition, &depthX, &depthY, imageFrameResolution );

   // 深度イメージの座標からカラーイメージの座標を取得する
   if( FAILED( hr = ::NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution(
                                                               imageFrameResolution, depthFrameResolution,
                                                               nullptr,
                                                               (LONG)depthX, (LONG)depthY,
                                                               0,
                                                               pColorX, pColorY ) ) )
      throw( UCException( hr, _T("TransformSkeletonToColorPixelCoordinate()でエラーが発生しました。カラーイメージの座標を取得できなかった") ) );
}

// Kinectから取得したスケルトンをDirect3D用に変換する
void UCPlane04_1::NextFrame( NUI_SKELETON_FRAME* pSrvSkeleton,
                             NUI_IMAGE_RESOLUTION imageFrameResolution, NUI_IMAGE_RESOLUTION depthFrameResolution,
                             float Width, float Height )
{
   // レンダリングするジョイント数
   UINT SkeletonCount = 0;

   LONG ColorX, ColorY;
   float x, y;

   for( int i=0; i<NUI_SKELETON_COUNT; i++ )
   {
      NUI_SKELETON_DATA* skeletonData = &pSrvSkeleton->SkeletonData[i];

      // スケルトンの追跡状態をチェックする
      switch( skeletonData->eTrackingState )
      {
      // スケルトンが追跡されている
      case NUI_SKELETON_TRACKED:
         for( int j=0; j<NUI_SKELETON_POSITION_COUNT; j++ )
         {
            // ジョイントの追跡状態をチェックする
            switch( skeletonData->eSkeletonPositionTrackingState[j] )
            {
            // ジョイントの座標が追跡されている
            case NUI_SKELETON_POSITION_TRACKED:
               TransformSkeletonToScreenCoordinate( &skeletonData->SkeletonPositions[j], imageFrameResolution, depthFrameResolution, &ColorX, &ColorY );
               x = TransformScreenToProjectionCoordinate( Width, (float)ColorX );
               y = -TransformScreenToProjectionCoordinate( Height, (float)ColorY );
               m_SkeletonFrameArray[SkeletonCount++] = CONSTANT_BUFFER( XMFLOAT4( x, y, 0, 0 ),  XMFLOAT4( 1.0f, 0.2f, 0.2f, 1.0f ) );
               break;
            // ジョイントの座標が推測されている
            case NUI_SKELETON_POSITION_INFERRED:
               TransformSkeletonToScreenCoordinate( &skeletonData->SkeletonPositions[j], imageFrameResolution, depthFrameResolution, &ColorX, &ColorY );
               x = TransformScreenToProjectionCoordinate( Width, (float)ColorX );
               y = -TransformScreenToProjectionCoordinate( Height, (float)ColorY );
               m_SkeletonFrameArray[SkeletonCount++] = CONSTANT_BUFFER( XMFLOAT4( x, y, 0, 0 ), XMFLOAT4( 0, 1.0f, 0.0f, 1.0f ) );
               break;
            // ジョイントの座標が追跡されていない
            default:
               break;
            }
         }
         break;
      // プレイヤーの位置情報のみ追跡されている
      case NUI_SKELETON_POSITION_ONLY:
         TransformSkeletonToScreenCoordinate( &skeletonData->Position, imageFrameResolution, depthFrameResolution, &ColorX, &ColorY );
         x = TransformScreenToProjectionCoordinate( Width, (float)ColorX );
         y = -TransformScreenToProjectionCoordinate( Height, (float)ColorY );
         m_SkeletonFrameArray[SkeletonCount++] = CONSTANT_BUFFER( XMFLOAT4( x, y, 0, 0 ), XMFLOAT4( 0, 1.0f, 0, 1.0f ) );
         break;
      // 追跡されていない
      case NUI_SKELETON_NOT_TRACKED:
         break;
      }
   }

   m_SkeletonFrameCount = SkeletonCount;
}

void UCPlane04_1::Render( ID3D11DeviceContext* pD3DDeviceContext )
{
   HRESULT hr = E_FAIL;

   // 頂点バッファ設定
   UINT stride = sizeof( UCPlane04_1::VERTEX );
   UINT offset = 0;
   pD3DDeviceContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &stride, &offset );

   // インデックスバッファ設定
   pD3DDeviceContext->IASetIndexBuffer( m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );

   // プリミティブ タイプおよびデータの順序に関する情報を設定
   pD3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

   // ピクセルシェーダーのサンプラーステートを設定する
   pD3DDeviceContext->PSSetSamplers( 0, 1, &m_pSamplerState );

   // デカールマップを設定する
   pD3DDeviceContext->PSSetShaderResources( 0, 1, &m_pSRView );

   for( UINT i=0; i<m_SkeletonFrameCount; i++ )
   {
      D3D11_MAPPED_SUBRESOURCE mappedResource;
      if( FAILED( hr = pD3DDeviceContext->Map( m_pConstantBuffers, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource ) ) )
          throw( UCException( -1, _T("UCPlane04_1::Render()でエラーが発生しました。Map()が失敗しました。") ) );

      CONSTANT_BUFFER* cbuffer = (CONSTANT_BUFFER*)(mappedResource.pData);
      ::CopyMemory( &cbuffer->Position, &m_SkeletonFrameArray[i].Position, sizeof( XMFLOAT4 ));
      ::CopyMemory( &cbuffer->Color, &m_SkeletonFrameArray[i].Color, sizeof( XMFLOAT4 ));
      pD3DDeviceContext->Unmap( m_pConstantBuffers, 0 );

      // 定数バッファを設定
      pD3DDeviceContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffers );
      pD3DDeviceContext->PSSetConstantBuffers( 0, 1, &m_pConstantBuffers );

      // インデックスバッファを使用した描画
      pD3DDeviceContext->DrawIndexed( 4, 0, 0 );
   }
}

ジョイントの座標を確認するための板ポリをレンダリングするためのメッシュクラスです。 ジョイントが追跡できているときは青くなり、正確なジョイント位置が検出できないときは赤い○になります。
プレイヤーの位置情報のみ検出されているときは緑になります。

---Sample04.hlsl---  ↑

// 定数バッファ
cbuffer cbSize : register( b0 )
{
   float4 g_VertexOffset : packoffset( c0 );   // 頂点の平行移動値
   float4 g_VertexColor  : packoffset( c1 );   // 頂点カラー値
};

// テクスチャー
Texture2D g_Tex : register( t0 );

// サンプラーステート
SamplerState  g_Sampler : register( s0 );

// 頂点シェーダーの入力パラメータ
struct VS_IN
{
   float3 pos    : POSITION;    // 頂点座標
   float2 texel  : TEXCOORD;    // テクセル
};

// 頂点シェーダーの出力パラメータ
struct VS_OUT_PS_IN
{
   float4 pos    : SV_POSITION;
   float2 texel  : TEXCOORD0;
   float4 color  : TEXCOORD1;
};

// 頂点シェーダー
VS_OUT_PS_IN VS_Main( VS_IN In )
{
   VS_OUT_PS_IN Out;

   Out.pos = float4( In.pos + g_VertexOffset.xyz, 1.0f );
   Out.texel = In.texel;
   Out.color = g_VertexColor;
   return Out;
}

// ピクセルシェーダ
float4 PS_Main( VS_OUT_PS_IN In ) : SV_TARGET
{
   // 色取得
   float4 col =  g_Tex.Sample( g_Sampler, In.texel ) * In.color;
   
   return col.gbra;
}

メッシュクラスは分けましたが、シェーダーソースはステップ数を減らすために共通にしています。

---UKinect04.h---  ↑

#ifndef KINECT04_H
#define KINECT04_H

#include "../../Common/Header/UCommon.h"
#include <NuiApi.h>
#include "../../Common/Header/UException.h"

// Kinect用ライブラリ
#pragma comment( lib, "Kinect10.lib" )

class UCKinect04
{
private:
   INuiSensor* m_pKinectSensor;
   HANDLE m_ImageStreamHandle, m_DepthStreamHandle;
   DWORD m_Flags;

   static const NUI_IMAGE_RESOLUTION m_ImageResolution = NUI_IMAGE_RESOLUTION_640x480;
   static const NUI_IMAGE_RESOLUTION m_DepthResolution = NUI_IMAGE_RESOLUTION_640x480;

   // 次のフレームのイメージフレーム作成
   void CreateImageFrame( HANDLE StreamHandle, NUI_IMAGE_FRAME* pImageFrame, NUI_LOCKED_RECT* pRect );

   // メモリ解放
   void ReleaseImageFrame( HANDLE StreamHandle, NUI_IMAGE_FRAME* ImageFrame );

public:
   UCKinect04();
   ~UCKinect04();
   void CreateKinect( DWORD Flags, BOOL NearModeEnaled );

   // カラーデータを取得する
   void NextFrameImageData( NUI_LOCKED_RECT* pRect );

   // スケルトンデータを取得する
   void NextFrameSkeletonData( NUI_SKELETON_FRAME* pSkeletonFrame );

   // イメージフレームの解像度を取得する
   inline void GetImageResolutionToSize( DWORD& pWidth, DWORD& pHeight )
   {
      ::NuiImageResolutionToSize( m_ImageResolution, pWidth, pHeight );
   }

   const NUI_IMAGE_RESOLUTION GetImageResolution(){ return m_ImageResolution; };
   const NUI_IMAGE_RESOLUTION GetDepthResolution(){ return m_DepthResolution; };
};

#endif

---UKinect04.cpp---  ↑

#include "../../Common/Header/UKinect04.h"

UCKinect04::UCKinect04()
{
   m_pKinectSensor = nullptr;
}

UCKinect04::~UCKinect04()
{
   if( m_pKinectSensor != nullptr )
      m_pKinectSensor->NuiShutdown();

   SAFE_RELEASE( m_pKinectSensor );
}

void UCKinect04::CreateKinect( DWORD Flags, BOOL NearModeEnaled )
{
   HRESULT hr = E_FAIL;
   int count = 0;

   m_Flags = Flags;

   // マシンに接続されている Kinectセンサー 数を取得する。
   if( FAILED( hr = ::NuiGetSensorCount( &count ) ) )
      throw( UCException( hr, _T("UCKinect04::CreateKinect()でエラーが発生しました。接続されているKinectセンサーの数を取得できません。") ) );

   if( count == 0 )
      throw( UCException( -1, _T("UCKinect04::CreateKinect()でエラーが発生しました。Kinectセンサーが接続されていません。") ) );

   // 指定したインデックスの INuiSensor のインスタンスを作成する
   // ここでは 0 番目のインスタンスのみ作成する
   if( FAILED( hr = ::NuiCreateSensorByIndex( 0, &m_pKinectSensor ) ) )
      throw( UCException( hr, _T("UCKinect04::CreateKinect()でエラーが発生しました。Kinectセンサーが接続されていません。") ) );

   HRESULT status = E_FAIL;

   for( int i=0; i<50; i++ )
   {
      TCHAR msg[256];

      // Kinectセンサー の接続状態を取得する
      status = m_pKinectSensor->NuiStatus();
   
      // 正常
      if( status == S_OK )
         break;
      // Kinectセンサーが接続されているが、初期化中
      // Kinectセンサー接続直後にアプリを起動するとこの状態になるときがある
      else if( status == S_NUI_INITIALIZING )
      {
         _stprintf_s( msg, _T("■□■HRESULT:0x%x [ %s ]\n"), hr, _T(":Kinectセンサーが接続されているが、初期化中\n") );
         OutputDebugString( msg );
      }
      // Kinectセンサーが接続されているが、エラーが発生
      // Kinectセンサー接続直後にアプリを起動するとこの状態になるときがある
      else
      {
         _stprintf_s( msg, _T("■□■HRESULT:0x%x [ %s ]\n"), hr, _T(":Kinectセンサーが接続されているが、エラーが発生\n") );
         OutputDebugString( msg );
      }

      Sleep( 1000 );
   }
   if( status != S_OK )
      throw( UCException( -1, _T("UCKinect04::CreateKinect()でエラーが発生しました。Kinectセンサーが接続されているが、初期化が完了しなかった") ) );

   // Kinectを初期化する。
   if( FAILED( hr = m_pKinectSensor->NuiInitialize( m_Flags ) ) )
      throw( UCException( -1, _T("UCKinect04::CreateKinect()でエラーが発生しました。Kinectの初期化に失敗した") ) );

   // RGBカメラを使用する場合
   if( m_Flags & NUI_INITIALIZE_FLAG_USES_COLOR )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_COLOR,                   // カラーデータを取得する
                            m_ImageResolution,                      // イメージストリームの解像度を指定する
                            0, 2, nullptr, 
                            &m_ImageStreamHandle                    // 開始されたイメージストリームのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect04::CreateKinect()でエラーが発生しました。イメージストリームをオープンできなかった") ) );
   }

   // 深度カメラを使用し、プレイヤーIDを使用しない場合
   if( m_Flags & NUI_INITIALIZE_FLAG_USES_DEPTH )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_DEPTH,                   // 深度データを取得する
                            m_DepthResolution,                      // 深度ストリームの解像度を指定する
                            0, 2, nullptr, 
                            &m_DepthStreamHandle                    // 開始された深度ストリームのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect04::CreateKinect()でエラーが発生しました。深度ストリームをオープンできなかった") ) );
   }

   // 深度カメラを使用し、プレイヤーIDを使用する場合
   else if( m_Flags & NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,  // 深度データとプレイヤーIDを取得する
                            m_DepthResolution,                      // 深度ストリームの解像度を指定する
                            0, 2, nullptr, 
                            &m_DepthStreamHandle                    // 開始された深度ストリーム + プレイヤーIDのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect04::CreateKinect()でエラーが発生しました。深度ストリーム + プレイヤーIDをオープンできなかった") ) );
   }

   // Nearモードを有効にする?
   if( NearModeEnaled )
   {
      // Kinect for XBOX は Nearモード は対応していない!
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamSetImageFrameFlags( m_DepthStreamHandle, NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE ) ) )
         throw( UCException( hr, _T("UCKinect04::CreateKinect()でエラーが発生しました。イメージフレームの設定に失敗した") ) );
   }

   // スケルトントラッキングを有効にする?
   if( Flags & NUI_INITIALIZE_FLAG_USES_SKELETON )
   {
      if( FAILED( hr = m_pKinectSensor->NuiSkeletonTrackingEnable( nullptr, 0 ) ) )
          throw( UCException( hr, _T("UCKinect04::CreateKinect()でエラーが発生しました。スケルトントラッキングを有効にできなかった") ) );
   }
}

void UCKinect04::CreateImageFrame( HANDLE StreamHandle, NUI_IMAGE_FRAME* pImageFrame, NUI_LOCKED_RECT* pRect )
{
   HRESULT hr = E_FAIL;
   NUI_LOCKED_RECT LockRect;

   ::ZeroMemory( pRect, sizeof( NUI_LOCKED_RECT ) );

   // 指定されたイメージストリームから次のイメージフレームを取得する
   if( FAILED( hr = m_pKinectSensor->NuiImageStreamGetNextFrame( StreamHandle, INFINITE, pImageFrame ) ) )
      throw( UCException( hr, _T("UCKinect04::CreateImageFrame()でエラーが発生しました。次のイメージフレームを取得できなかった") ) );

   // イメージフレームからカラーデータを取得する
   if( FAILED( hr = pImageFrame->pFrameTexture->LockRect( 0, &LockRect, nullptr, 0 ) ) )
      throw( UCException( hr, _T("UCKinect04::CreateImageFrame()でエラーが発生しました。イメージフレームからイメージデータを取得できなかった") ) );

   // コピー
   pRect->pBits = NEW BYTE[ LockRect.size ];
   ::CopyMemory( pRect->pBits, LockRect.pBits, sizeof( BYTE ) * LockRect.size );
   pRect->Pitch = LockRect.Pitch;
   pRect->size = LockRect.size;
}

void UCKinect04::ReleaseImageFrame( HANDLE StreamHandle, NUI_IMAGE_FRAME* ImageFrame )
{
   HRESULT hr = E_FAIL;

   // ロックを解除する
   if( FAILED( hr = ImageFrame->pFrameTexture->UnlockRect( 0 ) ) )
      throw( UCException( hr, _T("UCKinect04::ReleaseImageFrame()でエラーが発生しました。ロックを解除できなかった") ) );

   // イメージストリームから取得したイメージフレームを解放する
   if( FAILED( hr = m_pKinectSensor->NuiImageStreamReleaseFrame( StreamHandle, ImageFrame ) ) )
      throw( UCException( hr, _T("UCKinect04::ReleaseImageFrame()でエラーが発生しました。イメージストリームから取得したイメージフレームを解放できなかった") ) );
}

void UCKinect04::NextFrameImageData( NUI_LOCKED_RECT* pRect )
{
   NUI_IMAGE_FRAME ImageFrame;

   CreateImageFrame( m_ImageStreamHandle, &ImageFrame, pRect );
   ReleaseImageFrame( m_ImageStreamHandle, &ImageFrame );

   // 線形合成した場合でも正しくレンダリングできるようにRGBカメラの結果のアルファ値は常に255にする
   for( INT i=3; i<pRect->size; i+=4 )
      pRect->pBits[i] = 255;
}

// スケルトンデータを取得する
void UCKinect04::NextFrameSkeletonData( NUI_SKELETON_FRAME* pSkeletonFrame )
{
   HRESULT hr = E_FAIL;

   if( FAILED( hr = m_pKinectSensor->NuiSkeletonGetNextFrame( INFINITE, pSkeletonFrame ) ) )
      throw( UCException( hr, _T("UCKinect04::NextFrameSkeletonData()でエラーが発生しました。スケルトンの次のフレームのデータを取得できなかった") ) );
}

Kinect for Windowsセンサー ではNearモードというのがあります。 名前の通り近距離の深度値の検出が可能となるモードです。
しかし Kinect for XBOXセンサーでは対応していないため検証できていません。

---main.cpp---  ↑

#include "../../Common/Header/UCommon.h"
#include "../../Common/Header/UException.h"
#include "../../Common/Header/UDirect3D11.h"
#include "../../Common/Header/UGraphicsPipeline.h"
#include "../../Common/Header/UKinect04.h"
#include "../../Common/Header/UDDSLoader.h"
#include "UPlane04_0.h"
#include "UPlane04_1.h"
#include <NuiApi.h>

// Direct3D用定数バッファ設定用構造体
typedef struct _CONSTANT_BUFFER
{
   XMFLOAT4 Position;
   XMFLOAT4 Color;

   _CONSTANT_BUFFER(){};
   _CONSTANT_BUFFER( XMFLOAT4 position, XMFLOAT4 color )
   {
      ::CopyMemory( &Position, &position, sizeof( XMFLOAT4 ) );
      ::CopyMemory( &Color, &color, sizeof( XMFLOAT4 ) );
   };
}CONSTANT_BUFFER;

// シェーダーオブジェクトを作成するとき、ファイルから読むか、メモリから読むかを切り替える
#if defined(DEBUG) || defined(_DEBUG)
   #define UNCOMPILED_SHADER     // ファイルを読み込んでコンパイルする
#else
   #include "../../Common/HLSL/Sample04_VS_Main.h"
   #include "../../Common/HLSL/Sample04_PS_Main.h"
#endif

// アプリケーション名
TCHAR* AppName = _T("Kinect_Tutrial04");

// Direct3D関連の自作クラス
UCDirect3D11* g_pDirect3D11 = nullptr;

// グラフィックパイプライン関連の自作クラス
UCGraphicsPipeline* g_pGraphicsPipeline = nullptr;

// シェーダー用定数バッファ
ID3D11Buffer* g_pConstantBuffers[1] = { nullptr };

// Kinectセンサーオブジェクト
UCKinect04* g_pKinect = nullptr;

// 平面オブジェクト
UCPlane04_0* g_pPlane_0 = nullptr;
// ジョイント
UCPlane04_1* g_pPlane_1 = nullptr;

// メモリ解放
void Invalidate()
{
   for( int i=0; i<_countof( g_pConstantBuffers ); i++ )
      SAFE_RELEASE( g_pConstantBuffers[i] );

   SAFE_DELETE( g_pKinect );
   SAFE_DELETE( g_pPlane_0 );
   SAFE_DELETE( g_pPlane_1 );
   SAFE_DELETE( g_pGraphicsPipeline );
   SAFE_DELETE( g_pDirect3D11 );
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
   switch( msg )
   {
   case WM_KEYUP:
      // アプリ終了
      if( wParam == VK_ESCAPE )
         ::DestroyWindow( hWnd );
      break;

   case WM_DESTROY:
      ::PostQuitMessage(0);
      break;

   default:
      return ::DefWindowProc( hWnd, msg, wParam, lParam );
   }

   return 0L;
}

// Direct3Dの作成
void CreateDirect3D( HINSTANCE hInstance, DWORD Width, DWORD Height )
{
   DXGI_MODE_DESC* pModeDescArray = nullptr;

   __try
   {
      DXGI_MODE_DESC  ModeDesc;
      UINT num;

      // ディスプレイモード一覧の数を取得する
      g_pDirect3D11->GetDisplayMode( nullptr, &num );

      pModeDescArray = NEW DXGI_MODE_DESC[num];
      // ディスプレイモード一覧を取得する
      g_pDirect3D11->GetDisplayMode( pModeDescArray, &num );

      bool find = false;
      for( UINT i=0; i<num; i++ )
      {
         // 適切な解像度のディスプレイモードを検索する
         if( pModeDescArray[i].Width == Width && pModeDescArray[i].Height == Height )
         {
            find = true;
            ::CopyMemory( &ModeDesc, &pModeDescArray[i], sizeof( DXGI_MODE_DESC ) );
            break;
         }
      }

      if( find == false )
         throw( UCException( -1, _T("InitDirect3D()でエラーが発生しました。適切なディスプレイモードの解像度を取得できません。") ) );

      // ウィンドウの作成およびDirect3D の作成
      g_pDirect3D11->CreateDirect3D11( AppName, hInstance, WndProc, &ModeDesc, TRUE, TRUE );
   }
   __finally
   {
      SAFE_DELETE_ARRAY( pModeDescArray );
   }
}

void CreateGraphicePipeline()
{
   D3D11_INPUT_ELEMENT_DESC layout[] = {
         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };
   
   // シェーダーを作成する
#if defined(DEBUG) || defined(_DEBUG)
   g_pGraphicsPipeline->CreateVertexShaderFromFile( g_pDirect3D11->m_pD3DDevice, _T("../../Common/HLSL/Sample04.hlsl"), "VS_Main", layout, _countof( layout ) );
   g_pGraphicsPipeline->CreatePixelShaderFromFile(  g_pDirect3D11->m_pD3DDevice, _T("../../Common/HLSL/Sample04.hlsl"), "PS_Main" );
#else
   g_pGraphicsPipeline->CreateVertexShaderFromMemory( g_pDirect3D11->m_pD3DDevice, (LPBYTE)g_VS_Main, sizeof( g_VS_Main ), layout, _countof( layout ) );
   g_pGraphicsPipeline->CreatePixelShaderFromMemory(  g_pDirect3D11->m_pD3DDevice, (LPBYTE)g_PS_Main, sizeof( g_PS_Main ) );
#endif

   // ラスタライザーステートを作成する
   g_pGraphicsPipeline->CreateRasterizerState( g_pDirect3D11->m_pD3DDevice, D3D11_CULL_MODE::D3D11_CULL_BACK );

   // 深度ステンシルステートを作成する
   g_pGraphicsPipeline->CreateDepthStencilState( g_pDirect3D11->m_pD3DDevice, FALSE, D3D11_DEPTH_WRITE_MASK::D3D11_DEPTH_WRITE_MASK_ALL );

   // ブレンドステートを線形合成で作成する
   UCGraphicsPipeline::UEBLEND_STATE BlendStateType[1] = { UCGraphicsPipeline::UEBLEND_STATE::ALIGNMENT };
   g_pGraphicsPipeline->CreateBlendState( g_pDirect3D11->m_pD3DDevice, BlendStateType, 1 );

   // シェーダー用の定数バッファを作成する
   g_pConstantBuffers[0] = g_pGraphicsPipeline->CreateConstantBuffer( g_pDirect3D11->m_pD3DDevice, nullptr, sizeof( CONSTANT_BUFFER ), D3D11_CPU_ACCESS_WRITE );
}

// メッシュを作成する
void CreateMesh( DWORD Width, DWORD Height )
{
   // RGBカメラのイメージをレンダリングするためのメッシュを作成する
   g_pPlane_0->CreateMesh( g_pDirect3D11->m_pD3DDevice, Width, Height, g_pConstantBuffers );

   // ジョイントをレンダリングするためのメッシュを作成する
   // 矩形サイズは射影空間上でのサイズで指定する
   float size = 0.0001f;
   float x = size * (float)Height;
   float y = size * (float)Width;
   g_pPlane_1->CreateMesh( g_pDirect3D11->m_pD3DDevice, x, y, _T("res\\01.dds"), NUI_SKELETON_COUNT + NUI_SKELETON_COUNT * NUI_SKELETON_POSITION_COUNT, g_pConstantBuffers );
}

void CreateResource( HINSTANCE hInstance )
{
    // Direct3D 関連自作クラスのインスタンスを作成
    // CreateDirect3D()関数内でインスタンスの作成を行うと、C2712 エラーが発生するのでここで行う。
    g_pDirect3D11 = NEW UCDirect3D11();
    g_pGraphicsPipeline = NEW UCGraphicsPipeline();
    g_pPlane_0 = NEW UCPlane04_0();
    g_pPlane_1 = NEW UCPlane04_1();
    g_pKinect = NEW UCKinect04();

    DWORD Width = 0, Height = 0;

    // Kinectセンサーオブジェクトを作成
   // RGBカメラとプレイヤーIDを使用する
    g_pKinect->CreateKinect( NUI_INITIALIZE_FLAG_USES_COLOR | 
                             NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX |
                             NUI_INITIALIZE_FLAG_USES_SKELETON,
                             FALSE
                           );

   g_pKinect->GetImageResolutionToSize( Width, Height );

    // Direct3Dの作成
    CreateDirect3D( hInstance, Width, Height );

    // グラフィックパイプラインリソースの作成
    CreateGraphicePipeline();

    // リソースの作成
    CreateMesh( Width, Height );
}

void NextFrame()
{
   NUI_LOCKED_RECT imageDataFromKinect = { 0 };
   NUI_SKELETON_FRAME skeletonFrameFromKinect = { 0 };

   DWORD Width, Height;
   g_pKinect->GetImageResolutionToSize( Width, Height );

   __try
   {
     // *****************************************************************
     // イメージフレーム
     // *****************************************************************

      // Kinect側の次のフレームのイメージフレームの作成
      g_pKinect->NextFrameImageData( &imageDataFromKinect );

      // シェーダーリソースビューを作成する
      g_pPlane_0->NextFrame( g_pDirect3D11->m_pD3DDeviceContext, &imageDataFromKinect );

     // *****************************************************************
     // スケルトンフレーム
     // *****************************************************************

     // Kinect側の次のフレームのスケルトンデータの作成
     g_pKinect->NextFrameSkeletonData( &skeletonFrameFromKinect );

      // 定数バッファ設定用の変数の更新
      g_pPlane_1->NextFrame( &skeletonFrameFromKinect,
                             g_pKinect->GetImageResolution(), g_pKinect->GetDepthResolution(),
                             (float)Width, (float)Height );
   }

   __finally
   {
      SAFE_DELETE_ARRAY( imageDataFromKinect.pBits );
   }
}

// 描画処理
HRESULT Render()
{
   HRESULT hr = E_FAIL;
   float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };

    // バックバッファをクリアする。
   g_pDirect3D11->ClearBackBuffer( ClearColor ); 

   // 深度バッファをクリアする。
   g_pDirect3D11->ClearDepthStencilView( D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0 );

   // 各種グラフィックパイプラインを設定
   g_pGraphicsPipeline->SetVertexShader( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetPixelShader( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetRasterizerState( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetDepthStencilState( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetBlendState( g_pDirect3D11->m_pD3DDeviceContext );

   // レンダリング
   g_pPlane_0->Render( g_pDirect3D11->m_pD3DDeviceContext );
   g_pPlane_1->Render( g_pDirect3D11->m_pD3DDeviceContext );

   // レンダリングされたイメージをユーザーに表示。
   if( FAILED( hr = g_pDirect3D11->Present( 0, 0 ) ) )
      throw( UCException( hr, _T("IDXGISwapChain::Present()が失敗しました") ) );

   return hr;
}
 
// メイン関数
int APIENTRY _tWinMain( HINSTANCE hInstance,
                        HINSTANCE,
                        LPTSTR,
                        INT )
{
   // メモリリーク検出
   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_EVERY_1024_DF);

   MSG msg = {0};

   try
   {
      CreateResource( hInstance );

      ::ShowWindow( g_pDirect3D11->m_hWnd, SW_SHOW );
      ::UpdateWindow( g_pDirect3D11->m_hWnd );

      // メッセージループ
      do
      { 
        if( ::PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
        {
          ::TranslateMessage(&msg); 
          ::DispatchMessage(&msg); 
        }
        else
        {
          NextFrame();
          Render();
        }
      }while( msg.message != WM_QUIT );
   }
   catch( UCException ex )
   {
      ::MessageBox( nullptr, ex.m_pErrorStr, _T("エラー"), MB_OK );
   }

   Invalidate();
   ::UnregisterClass( AppName, hInstance );

   return msg.wParam;
}

今回は疲れ気味でKinect for Windows SDKのリファレンスをチェックしてません。なのでどっか間違ってるかも。

web拍手 by FC2

Prev Top Next

inserted by FC2 system