新規記事の投稿を行うことで、非表示にすることが可能です。
2018年08月20日
《その437》シェーダ(点光源の場合)(4)
シェーダ(点光源の場合)(4)
ファイル(F) -> 新規作成(N) -> プロジェクト(P)
Visual C++ -> Windowsユニバーサル -> DirectX11アプリ(ユニバーサルWindows)
の順に操作して VS2017 を開始すると、3Dアニメーションのサンプルアプリが自動作成されます。
このサンプルアプリを改変することで、DirectX11アプリ(ユニバーサルWindows) について、ほんの僅かではありますが 学んできました。
直近の3回( 《434》~《436》 )も、点光源をつかって立方体に光を照射する場合についての内容だったので、前回までに扱ったコードと重複してしまいますが、関連するプログラムコードを まとめて載せておきます。
下記のプログラムにより、次のような画像が表示されます。
以下は、Sample3DSceneRenderer.cpp です。
#include "pch.h"
#include "Sample3DSceneRenderer.h"
#include "..\Common\DirectXHelper.h"
using namespace App20;
using namespace DirectX;
using namespace Windows::Foundation;
// ファイルから頂点とピクセルシェーダを読み込み、キューブのジオメトリをインスタンス化します。
Sample3DSceneRenderer::Sample3DSceneRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
m_loadingComplete(false),
m_degreesPerSecond(45),
m_indexCount(0),
m_tracking(false),
m_deviceResources(deviceResources)
{
CreateDeviceDependentResources();
CreateWindowSizeDependentResources();
}
// ウィンドウのサイズが変更されたときに、ビューのパラメータを初期化します。
void Sample3DSceneRenderer::CreateWindowSizeDependentResources()
{
Size outputSize = m_deviceResources->GetOutputSize();
float aspectRatio = outputSize.Width / outputSize.Height;
float fovAngleY = 70.0f * XM_PI / 180.0f;
// これは、アプリケーションが縦向きビューまたはスナップビュー内にあるときに行うことのできる変更の簡単な例です。
if (aspectRatio < 1.0f)
{
fovAngleY *= 2.0f;
}
// OrientationTransform3D マトリックスは、シーンの方向を表示方向と正しく一致させるため、ここで事後乗算されます。
// この事後乗算ステップは、スワップチェーンのターゲットビットマップに対して行われるすべての描画呼び出しで実行する必要があります。
// 他のターゲットに対する呼び出しでは、適用する必要はありません。
// このサンプルでは、行優先のマトリックスを使用した右辺座標系を使用しています。
XMMATRIX perspectiveMatrix = XMMatrixPerspectiveFovRH(
fovAngleY,
aspectRatio,
0.01f,
100.0f
);
XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();
XMMATRIX orientationMatrix = XMLoadFloat4x4(&orientation);
XMStoreFloat4x4(
&m_constantBufferData.projection,
XMMatrixTranspose(perspectiveMatrix * orientationMatrix)
);
// 視点は (0,0.9,1.5) の位置にあり、y軸に沿って上方向のポイント (0,-0.1,0) を見ています。
static const XMVECTORF32 eye = { 0.0f, 0.9f, 1.5f, 0.0f };
static const XMVECTORF32 at = { 0.0f, -0.1f, 0.0f, 0.0f };
static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };
XMStoreFloat4x4(&m_constantBufferData.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
}
// フレームごとに 1回呼び出し、キューブを回転させてから、モデルおよびビューのマトリックスを計算します。
void Sample3DSceneRenderer::Update(DX::StepTimer const& timer)
{
if (!m_tracking)
{
// 度をラジアンに変換し、秒を回転角度に変換します。
float radiansPerSecond = XMConvertToRadians(m_degreesPerSecond);
double totalRotation = timer.GetTotalSeconds() * radiansPerSecond;
float radians = static_cast<float>(fmod(totalRotation, XM_2PI));
Rotate(radians);
}
}
// 3Dキューブモデルを、ラジアン単位で設定された大きさだけ回転させます。
void Sample3DSceneRenderer::Rotate(float radians)
{
// 更新されたモデルマトリックスをシェーダに渡す準備をします。
XMStoreFloat4x4(&m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(radians)));
}
void Sample3DSceneRenderer::StartTracking()
{
m_tracking = true;
}
// 追跡時に、出力画面の幅方向を基準としてポインタの位置を追跡することにより、3Dキューブを Y軸に沿って回転させることができます。
void Sample3DSceneRenderer::TrackingUpdate(float positionX)
{
if (m_tracking)
{
float radians = XM_2PI * 2.0f * positionX / m_deviceResources->GetOutputSize().Width;
Rotate(radians);
}
}
void Sample3DSceneRenderer::StopTracking()
{
m_tracking = false;
}
// 頂点とピクセルシェーダを使用して、1つのフレームを描画します。
void Sample3DSceneRenderer::Render()
{
// 読み込みは非同期です。読み込みが完了した後にのみ描画してください。
if (!m_loadingComplete)
{
return;
}
auto context = m_deviceResources->GetD3DDeviceContext();
// 定数バッファを準備して、グラフィックスデバイスに送信します。
context->UpdateSubresource1(
m_constantBuffer.Get(),
0,
NULL,
&m_constantBufferData,
0,
0,
0
);
// 各頂点は、VertexPositionColor構造体の 1つのインスタンスです。
UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;
context->IASetVertexBuffers(
0,
1,
m_vertexBuffer.GetAddressOf(),
&stride,
&offset
);
context->IASetIndexBuffer(
m_indexBuffer.Get(),
DXGI_FORMAT_R16_UINT, // 各インデックスは、1つの 16ビット符号なし整数(short) です。
0
);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->IASetInputLayout(m_inputLayout.Get());
// 頂点シェーダをアタッチします。
context->VSSetShader(
m_vertexShader.Get(),
nullptr,
0
);
// 定数バッファをグラフィックスデバイスに送信します。
context->VSSetConstantBuffers1(
0,
1,
m_constantBuffer.GetAddressOf(),
nullptr,
nullptr
);
// ピクセルシェーダをアタッチします。
context->PSSetShader(
m_pixelShader.Get(),
nullptr,
0
);
// オブジェクトを描画します。
context->DrawIndexed(
m_indexCount,
0,
0
);
}
void Sample3DSceneRenderer::CreateDeviceDependentResources()
{
// シェーダを非同期で読み込みます。
auto loadVSTask = DX::ReadDataAsync(L"SampleVertexShader.cso");
auto loadPSTask = DX::ReadDataAsync(L"SamplePixelShader.cso");
// 頂点シェーダファイルを読み込んだ後、シェーダと入力レイアウトを作成します。
auto createVSTask = loadVSTask.then([this](const std::vector<byte>& fileData) {
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateVertexShader(
&fileData[0],
fileData.size(),
nullptr,
&m_vertexShader
)
);
static const D3D11_INPUT_ELEMENT_DESC vertexDesc [] =
{
// DXGI_FORMAT_R32G32B32_FLOAT ・・・ 3成分_96ビット浮動小数点フォーマット
// (カラーチャネルあたり32ビット)
// D3D11_INPUT_PER_VERTEX_DATA ・・・ 入力データは頂点単位のデータ
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
/*
※ https://msdn.microsoft.com/ja-jp/library/ee416244(v=vs.85).aspx
typedef struct D3D11_INPUT_ELEMENT_DESC {
LPCSTR SemanticName;
UINT SemanticIndex;
DXGI_FORMAT Format;
UINT InputSlot;
UINT AlignedByteOffset;
D3D11_INPUT_CLASSIFICATION InputSlotClass;
UINT InstanceDataStepRate;
} D3D11_INPUT_ELEMENT_DESC;
SemanticName
シェーダー入力署名でこの要素に関連付けられている HLSL セマンティクスです。
SemanticIndex
要素のセマンティクス インデックスです。
セマンティクス インデックスは、整数のインデックス番号によってセマンティクスを修飾するものです。
セマンティクス インデックスは、同じセマンティクスの要素が複数ある場合にのみ必要です。
たとえば、4x4 のマトリクスには 4 個の構成要素があり、
それぞれの構成要素にはセマンティクス名として matrix が付けられますが、
4 個の構成要素にはそれぞれ異なるセマンティクス インデックス (0、1、2、3) が割り当てられます。
Format
要素データのデータ型です。「DXGI_FORMAT」を参照してください。
InputSlot
入力アセンブラーを識別する整数値です (「入力スロット」を参照してください)。
有効な値は 0 〜 15 であり、D3D11.h で定義されています。
AlignedByteOffset
(省略可能)各要素間のオフセット (バイト単位) です。
前の要素の直後で現在の要素を定義するには、D3D11_APPEND_ALIGNED_ELEMENT を使用すると便利です。
必要に応じてパッキング処理も指定できます。
InputSlotClass
単一の入力スロットの入力データ クラスを識別します (「D3D11_INPUT_CLASSIFICATION」を参照してください)。
InstanceDataStepRate
バッファーの中で要素の 1 つ分進む前に、インスタンス単位の同じデータを使用して描画するインスタンスの数です。
頂点単位のデータを持つ要素 (スロット クラスは D3D11_INPUT_PER_VERTEX_DATA に設定されています) では、
この値が 0 であることが必要です。
*/
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateInputLayout(
vertexDesc,
ARRAYSIZE(vertexDesc),
&fileData[0],
fileData.size(),
&m_inputLayout
)
);
});
// ピクセルシェーダファイルを読み込んだ後、シェーダと定数バッファを作成します。
auto createPSTask = loadPSTask.then([this](const std::vector<byte>& fileData) {
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreatePixelShader(
&fileData[0],
fileData.size(),
nullptr,
&m_pixelShader
)
);
CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer) , D3D11_BIND_CONSTANT_BUFFER);
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateBuffer(
&constantBufferDesc,
nullptr,
&m_constantBuffer
)
);
});
// 両方のシェーダの読み込みが完了したら、メッシュを作成します。
auto createCubeTask = (createPSTask && createVSTask).then([this] () {
// メッシュの頂点座標と頂点法線ベクトルを読み込みます。
static VertexPositionColor verticesPosNrm[24];
verticesPosNrm[ 0].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[ 1].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[ 2].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[ 3].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[ 4].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[ 5].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[ 6].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[ 7].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
verticesPosNrm[ 8].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[ 9].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[10].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[11].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[12].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[13].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[14].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[15].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
verticesPosNrm[16].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[17].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[18].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[19].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[20].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[21].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[22].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[23].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
verticesPosNrm[ 0].col = XMFLOAT3( 0.6f, 0.6f, 1.0f);
verticesPosNrm[ 1].col = XMFLOAT3( 0.6f, 0.6f, 1.0f);
verticesPosNrm[ 2].col = XMFLOAT3( 0.6f, 0.6f, 1.0f);
verticesPosNrm[ 3].col = XMFLOAT3( 0.6f, 0.6f, 1.0f);
verticesPosNrm[ 4].col = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 5].col = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 6].col = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 7].col = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 8].col = XMFLOAT3( 1.0f, 1.0f, 0.0f);
verticesPosNrm[ 9].col = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[10].col = XMFLOAT3( 1.0f, 1.0f, 0.0f);
verticesPosNrm[11].col = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[12].col = XMFLOAT3( 1.0f, 1.0f, 0.0f);
verticesPosNrm[13].col = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[14].col = XMFLOAT3( 1.0f, 1.0f, 0.0f);
verticesPosNrm[15].col = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[16].col = XMFLOAT3( 1.0f, 0.0f, 1.0f);
verticesPosNrm[17].col = XMFLOAT3( 1.0f, 0.0f, 1.0f);
verticesPosNrm[18].col = XMFLOAT3( 0.0f, 1.0f, 1.0f);
verticesPosNrm[19].col = XMFLOAT3( 0.0f, 1.0f, 1.0f);
verticesPosNrm[20].col = XMFLOAT3( 1.0f, 0.0f, 1.0f);
verticesPosNrm[21].col = XMFLOAT3( 1.0f, 0.0f, 1.0f);
verticesPosNrm[22].col = XMFLOAT3( 0.0f, 1.0f, 1.0f);
verticesPosNrm[23].col = XMFLOAT3( 0.0f, 1.0f, 1.0f);
verticesPosNrm[ 0].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 1].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 2].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 3].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 4].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 5].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 6].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 7].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 8].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[ 9].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[10].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[11].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[12].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[13].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[14].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[15].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[16].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[17].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[18].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[19].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[20].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[21].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[22].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[23].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
vertexBufferData.pSysMem = verticesPosNrm;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(verticesPosNrm), D3D11_BIND_VERTEX_BUFFER);
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateBuffer(
&vertexBufferDesc,
&vertexBufferData,
&m_vertexBuffer
)
);
// メッシュのインデックスを読み込みます。インデックスの 3つ 1組の値のそれぞれは、画面上に描画される三角形を表します。
// たとえば、0,2,1 とは、頂点バッファのインデックス 0, 2, 1 にある頂点が、このメッシュの最初の三角形を構成することを意味します。
static const unsigned short cubeIndices [] =
{
0,2,1, // -x
1,2,3,
4,5,6, // +x
5,7,6,
16,17,21, // -y
16,21,20,
18,22,23, // +y
18,23,19,
8,12,10, // -z
10,12,14,
9,11,15, // +z
9,15,13,
};
m_indexCount = ARRAYSIZE(cubeIndices);
D3D11_SUBRESOURCE_DATA indexBufferData = {0};
indexBufferData.pSysMem = cubeIndices;
indexBufferData.SysMemPitch = 0;
indexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER);
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateBuffer(
&indexBufferDesc,
&indexBufferData,
&m_indexBuffer
)
);
});
// キューブが読み込まれたら、オブジェクトを描画する準備が完了します。
createCubeTask.then([this] () {
m_loadingComplete = true;
});
}
void Sample3DSceneRenderer::ReleaseDeviceDependentResources()
{
m_loadingComplete = false;
m_vertexShader.Reset();
m_inputLayout.Reset();
m_pixelShader.Reset();
m_constantBuffer.Reset();
m_vertexBuffer.Reset();
m_indexBuffer.Reset();
}
以下は、ShaderStructures.h です。
#pragma once
namespace App20
{
// MVPマトリックスを頂点シェーダに送信するために使用する定数バッファ
// MVP_Matrix
// Model_Matrix(モデル変換行列), View_Matrix(ビュー変換行列), Projection_Matrix(プロジェクション変換行列)
struct ModelViewProjectionConstantBuffer
{
DirectX::XMFLOAT4X4 model;
DirectX::XMFLOAT4X4 view;
DirectX::XMFLOAT4X4 projection;
};
// 頂点シェーダへの頂点ごとのデータの送信に使用
struct VertexPositionColor
{
DirectX::XMFLOAT3 pos;
DirectX::XMFLOAT3 col;
DirectX::XMFLOAT3 nrm;
};
}
以下は、SampleVertexShader.hlsl です。
// ジオメトリを作成するために 3つの基本的な列優先のマトリックスを保存する定数バッファ
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix model;
matrix view;
matrix projection;
};
// 頂点シェーダへの入力として使用する頂点ごとのデータ
struct VertexShaderInput
{
float3 pos : POSITION;
float3 col : COLOR;
float3 nrm : NORMAL;
};
// ピクセルシェーダを通じて渡されるピクセルごとのデータ
struct PixelShaderInput
{
float4 pos : SV_POSITION; // レンダリングパイプラインで加工済みの点座標
float3 pos_ : POSITION; // 未処理のままの点座標
float3 col : COLOR;
float3 nrm : NORMAL;
};
// GPU で頂点処理を行うための簡単なシェーダ
PixelShaderInput main(VertexShaderInput input)
{
PixelShaderInput output;
float4 pos_ = float4(input.pos, 1.0f);
pos_ = mul(pos_, model);
output.pos_ = pos_.xyz;
float4 pos = float4(input.pos, 1.0f);
pos = mul(pos, model);
pos = mul(pos, view);
pos = mul(pos, projection);
output.pos = pos;
output.col = input.col;
float4 nrm = float4(normalize(input.nrm), 0.0f);
nrm = mul(nrm, model);
output.nrm = normalize(nrm.xyz);
return output;
}
以下は、SamplePixelShader.hlsl です。
// ピクセルシェーダを通じて渡されるピクセルごとのデータ
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 pos_ : POSITION;
float3 col : COLOR;
float3 nrm : NORMAL;
};
// (補間済み)色データのパススルー関数
float4 main(PixelShaderInput input) : SV_TARGET
{
// light処理をしたピクセルの色成分を返します。
float3 light = float3(0.0, 0.8, 1.1); // 点光源の位置
float3 lightDirection = input.pos_ - light; // 光の方向ベクトル
float len = length(lightDirection); // 光の方向ベクトルを正規化(大きさを 1 にします)
float4 col = float4(input.col, 1.0);
// 正規化した法線ベクトル と 正規化した光の方向ベクトル のなす角から、
// その点の明るさの程度(0 〜 1)を算出。
float lightMagnitude = saturate(dot(input.nrm, -normalize(lightDirection)));
// 物理的には、k は距離の2乗に反比例するべきですが、
// ここでは出力画像が自分のイメージに合うように 式を変形しています。
float k = saturate(1.0f / (0.2f + 1.0f * len + 1.0 * len * len));
// k と lightMagnitude の両方の効果を使用しています。
// 光が当たらない部分にも 0.2 の割合の光を与えて真っ黒にならないようにしています。
return col * (0.8 * k * lightMagnitude + 0.2f);
}
2018年08月13日
《その436》シェーダ(点光源の場合)(3)
シェーダ(点光源の場合)(3)
前回《435》は 下記 (a) の効果を、前々回《434》は 下記 (b) の効果を、それぞれ使って陰影処理をしました。
今回は、(a),(b) 両方の効果を使った陰影処理です。
(a) 点光源からの光は、光源から離れるにつれて広い面積に拡散するので、明るさは減少します。
(b) ある面に光が当たるとき、光が面に垂直に当たるときが最も明るく、斜めに当たる場合は明るさが減少します。
点光源の座標 (0.0, 0.2, 0.7)
左側面と上面には光が当たりませんが、真っ黒にならないように 全体に 0.2 の割合の光を与えています。
点光源の座標 (0.0, 0.2, 0.8)
光源を少し手前に引きましたが、まだ左側面には光が当たりません。
点光源の座標 (0.0, 0.2, 0.9)
左側面にも光が当たるようになりました。右側面より暗いのは (b) の効果によるものです。
点光源の座標 (0.0, 0.2, 1.0)
光源をさらに手前に引きます。(b) の効果による明るさの差が少なくなりました。
点光源の座標 (0.0, 0.2, 1.1)
光源をさらに手前に引きます。
点光源の座標 (0.0, 0.2, 1.2)
光源をさらに手前に引きます。
点光源の座標 (0.0, 0.2, 1.4)
光源をさらに手前に引きます。(a) の効果で全体がかなり暗くなってきています。
点光源の座標 (0.0, 0.2, 1.8)
全体がさらに暗くなりました。
点光源の座標 (0.0, 0.2, 2.5)
全体がさらに暗くなりました。
以下は、SamplePixelShader.hlsl です。
// ピクセルシェーダを通じて渡されるピクセルごとのデータ
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 pos_ : POSITION;
float3 nrm : NORMAL;
};
// (補間済み)色データのパススルー関数
float4 main(PixelShaderInput input) : SV_TARGET
{
// light処理をしたピクセルの色成分を返します。
float3 light = float3(0.0, 0.2, 2.5); // 点光源の位置
float3 lightDirection = input.pos_ - light; // 光の方向ベクトル
float len = length(lightDirection); // 光の方向ベクトルを正規化(大きさを 1 にします)
// (b) の効果
// 正規化した法線ベクトル と 正規化した光の方向ベクトル のなす角から、
// その点の明るさの程度(0 〜 1)を算出。
float lightMagnitude = saturate(dot(input.nrm, -normalize(lightDirection)));
// (a) の効果
// 物理的には、k は距離の2乗に反比例するべきですが、
// ここでは出力画像が自分のイメージに合うように 式を変形しています。
float k = saturate(1.0f / (1.0f + 0.5f * len + 1.5 * len * len));
// k と lightMagnitude の両方の効果を使用しています。
// 光が当たらない部分にも 0.2 の割合の光を与えて真っ黒にならないようにしています。
// 立方体の色は、RGB が 255*1.0, 255*0.5, 255*0.6 です。
return float4(1.0, 0.5, 0.6, 1.0) * (0.8 * k * lightMagnitude + 0.2f);
}
2018年08月11日
《その435》シェーダ(点光源の場合)(2)
シェーダ(点光源の場合)(2)
前回《434》では、下記 (b) の効果について扱いました。
(a) 点光源からの光は、光源から離れるにつれて広い面積に拡散するので、明るさは減少します。
(b) ある面に光が当たるとき、光が面に垂直に当たるときが最も明るく、斜めに当たる場合は明るさが減少します。
(b) の効果のみで陰影処理を行うと、次のような不具合が生じます。
以下の3つの画像は、光源を少しずつ遠ざけていったときの様子です。光源が遠くなるにつれて、入射角が小さくなる(入射光と面のなす角が大きくなる)ので、画像が明るくなってしまっています。
光源が遠くなれば照らされる面の明るさは減少しなければなりません。
点光源の座標 (0.0, 0.7, 1.0) ・・・(b) の効果のみで描画しています。
点光源の座標 (0.0, 0.8, 1.5) ・・・(b) の効果のみで描画しています。
点光源の座標 (0.0, 0.9, 2.0) ・・・(b) の効果のみで描画しています。
(a) の効果
面の明るさは、光源からの距離の2乗に反比例します。実際に この法則通りに明るさを決めると、明るさの減り方が 期待通りにならない場合が多いので、計算式を少し変えることが多いようです。
以下の3つの画像は、(a) の効果のみで陰影処理を行った場合の結果です。とりあえず法則通りに、面の明るさが 光源からの距離の2乗に反比例するようにしてあります。
期待通り、出力された画像は、光源から遠くなるにつれて暗くなっています(ただし、光が面に当たる角度は考慮されていません)。
点光源の座標 (0.0, 0.7, 1.0) ・・・(a) の効果のみで描画しています。
点光源の座標 (0.0, 0.8, 1.5) ・・・(a) の効果のみで描画しています。
点光源の座標 (0.0, 0.9, 2.0) ・・・(a) の効果のみで描画しています。
(b) の効果を取り消して (a) の効果のみにするための、プログラム上の変更点は、
次の SamplePixelShader.hlsl の赤文字箇所のみです。
以下は、SamplePixelShader.hlsl です。
// ピクセルシェーダを通じて渡されるピクセルごとのデータ
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 pos_ : POSITION;
float3 nrm : NORMAL;
};
//(補間済み)色データのパススルー関数
float4 main(PixelShaderInput input) : SV_TARGET
{
// light処理をしたピクセルの色成分を返します。
float3 light = float3(0.0, 0.9, 2.0); // 点光源の位置
float3 lightDirection = input.pos_ - light; // 光の方向ベクトル
float len = length(lightDirection); // 光の方向ベクトルを正規化(大きさを 1 にします)
// 正規化した法線ベクトル と 正規化した光の方向ベクトル のなす角から、その点の明るさの程度(0 〜 1)を算出。
// float lightMagnitude = saturate(dot(input.nrm, -normalize(lightDirection)));
// k は距離の2乗に反比例する係数です。比例定数を 0.8 にしてみました。
float k = saturate(0.8f / len / len);
// return float4(0,1,0,0) * lightMagnitude; // 緑色(R,G,B が 0,1,0)に明るさの程度を乗じます。
// ここでは k の効果のみを使いますが、実際には、k と lightMagnitude を合わせて使用します(次回《436》で 確認します)。
return float4(0, 1, 0, 0) * k;
}
2018年08月10日
《その434》シェーダ(点光源の場合)(1)
シェーダ(点光源の場合)(1)
点光源とは、発光部の大きさが点とみなせるほど小さく、かつ被照射面との距離が比較的近い 光源のことです。今回のプログラムでは、光源を実際に点として考えます。
(a) 点光源からの光は、光源から離れるにつれて広い面積に拡散するので、明るさは減少します。
(b) ある面に光が当たるとき、光が面に垂直に当たるときが最も明るく、斜めに当たる場合は明るさが減少します。
(a)の効果は、光源と面との距離を用いることで、比較的 簡単に処理することができます。
(b)の効果は、光の向きと面の法線の向きを考えることで、処理することができます。
今回は、(b) の効果のみを扱います。
(a) の効果については、次回に見てみます。
今回も、これまでと同じ立方体を使います。
あらためて、立方体の各頂点の座標を確認しておきます。原点が立方体の中心になっています。
次に、いくつかの出力結果を示しておきます。
点光源の座標が (0.0, 0.8, 0.0) のときの様子です(真上からの照射です)。
光が当たらない面は真っ黒になっています。
点光源の座標が (0.0, 1.5, 2.0) のときの様子です(手前やや上からの照射です)。
点光源の座標が (0.0, 0.0, 1.0) のときの様子です(正面からの照射です)。
点光源の座標が (0.6, 0.7, 0.7) のときの様子です(手前右上からの照射です)。
以下は、SampleVertexShader.hlsl のコードです。
// ジオメトリを作成するために 3つの基本的な列優先のマトリックスを保存する定数バッファ
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix model;
matrix view;
matrix projection;
};
// 頂点シェーダへの入力として使用する頂点ごとのデータ
struct VertexShaderInput
{
float3 pos : POSITION;
float3 nrm : NORMAL;
};
// ピクセルシェーダを通じて渡されるピクセルごとのデータ
struct PixelShaderInput
{
float4 pos : SV_POSITION; // レンダリングパイプラインで加工済みの点座標
float3 pos_ : POSITION; // 未処理のままの点座標
float3 nrm : NORMAL;
};
// GPU で頂点処理を行うための簡単なシェーダ
PixelShaderInput main(VertexShaderInput input)
{
PixelShaderInput output;
float4 pos_ = float4(input.pos, 1.0f);
pos_ = mul(pos_, model);
output.pos_ = pos_.xyz;
float4 pos = float4(input.pos, 1.0f);
pos = mul(pos, model);
pos = mul(pos, view);
pos = mul(pos, projection);
output.pos = pos;
float4 nrm = float4(normalize(input.nrm), 0.0f);
nrm = mul(nrm, model);
output.nrm = normalize(nrm.xyz);
return output;
}
以下は、SamplePixelShader.hlsl のコードです。
// ピクセルシェーダを通じて渡されるピクセルごとのデータ
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 pos_ : POSITION;
float3 nrm : NORMAL;
};
// (補間済み)色データのパススルー関数
float4 main(PixelShaderInput input) : SV_TARGET
{
// light処理をしたピクセルの色成分を返します。
float3 light = float3(0.6, 0.7, 0.7); // 点光源の位置
float3 lightDirection = input.pos_ - light; // 光の方向ベクトル
float len = length(lightDirection); // 光の方向ベクトルを正規化(大きさを 1 にします)
// 正規化した法線ベクトル と 正規化した光の方向ベクトル のなす角から、その点の明るさの程度(0 〜 1)を算出。
float lightMagnitude = saturate(dot(input.nrm, -normalize(lightDirection)));
return float4(0,1,0,0) * lightMagnitude; // 緑色(R,G,B が 0,1,0)に明るさの程度を乗じます。
}
2018年08月07日
《その433》シェーダ(平行光線の場合)(2)
シェーダ(平行光線の場合)
本ブログ《431》の続きです。
「VS2017 VC++ DirectX11アプリ(ユニバーサルWindows)」のサンプルアプリに陰影処理を加えるため、《431》では、
Sample3DSceneRenderer.cpp
に 頂点法線ベクトルのデータを加えました。
今回は、
ShaderStructures.h
SamplePixelShader.hlsl
SampleVertexShader.hlsl
の改変についてです(改変は ほんの一部だけです)。
SamplePixelShader.hlsl と SampleVertexShader.hlsl には、
float4 pos : SV_POSITION;
float3 nrm : NORMAL;
などの 見慣れない記述が出てきます。
これは、これらのファイルが HLSL というプログラム言語で書かれているためです。HLSL は C++ に似ているので、なんとなく分かる気がする言語です。
以下は、ShaderStructures.h のコードです。
#pragma once
namespace App20
{
// MVPマトリックスを頂点シェーダに送信するために使用する定数バッファ
// MVP_Matrix
// Model_Matrix(モデル変換行列), View_Matrix(ビュー変換行列), Projection_Matrix(プロジェクション変換行列)
struct ModelViewProjectionConstantBuffer
{
DirectX::XMFLOAT4X4 model;
DirectX::XMFLOAT4X4 view;
DirectX::XMFLOAT4X4 projection;
};
// 頂点シェーダへの頂点ごとのデータの送信に使用
struct VertexPositionColor
{
DirectX::XMFLOAT3 pos;
DirectX::XMFLOAT3 nrm;
};
}
以下は、SamplePixelShader.hlsl のコードです。
// ピクセルシェーダを通じて渡されるピクセルごとの色データ
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 nrm : NORMAL;
};
// (補間済み)色データのパススルー関数
float4 main(PixelShaderInput input) : SV_TARGET
{
// light処理をしたピクセルの色成分を返します。
float3 lightDirection = normalize(float3(1, -0.5, -0.2)); // 平行光線の方向ベクトルです。
float lightMagnitude = 0.6f * saturate(dot(input.nrm, -lightDirection)) + 0.4f;
return float4(0,1,0,0) * lightMagnitude;
}
以下は、SampleVertexShader.hlsl のコードです。
// ジオメトリを作成するために 3つの基本的な列優先のマトリックスを保存する定数バッファ
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix model;
matrix view;
matrix projection;
};
// 頂点シェーダへの入力として使用する頂点ごとのデータ
struct VertexShaderInput
{
float3 pos : POSITION;
float3 nrm : NORMAL;
};
// ピクセルシェーダを通じて渡されるピクセルごとの色データ
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 nrm : NORMAL;
};
// GPU で頂点処理を行うための簡単なシェーダ
PixelShaderInput main(VertexShaderInput input)
{
PixelShaderInput output;
float4 pos = float4(input.pos, 1.0f);
pos = mul(pos, model);
pos = mul(pos, view);
pos = mul(pos, projection);
output.pos = pos;
float4 nrm = float4(normalize(input.nrm), 0.0f);
nrm = mul(nrm, model);
output.nrm = normalize(nrm.xyz);
return output;
}
2018年08月05日
《その432》面法線ベクトル,頂点法線ベクトル
面法線ベクトル,頂点法線ベクトル
面に垂直な直線を法線と呼びます。法線方向のベクトルを法線ベクトルといいますが、面から遠ざかる向きが正の向きです。
下図において、メッシュを構成する三角形の中の、例えば三角形576の法線ベクトル(面法線ベクトル)は薄紫色で表される右向き(x軸の正の向き)の矢印です。
したがって、 三角形576の面法線ベクトルは(1.0f, 0.0f, 0.0f)です。
※通常、法線ベクトルは、大きさ(長さ)が 1 になるように決めます。
頂点法線ベクトルというのは、頂点を始点とするベクトルです。三角形576のように独立した平面の場合、3つの頂点法線ベクトルは全て面に垂直になります。
よって、各頂点における頂点法線ベクトルは下図の薄オレンジ色で表される右向き矢印です。
この場合の頂点法線ベクトルは、面法線ベクトルと同じで、(1.0f, 0.0f, 0.0f)ということになります。
面法線ベクトルは、光線の向きを表すベクトルとのなす角度を計算することによって面の明るさを決定する ために利用されます。
前回《431》の Sample3DSceneRenderer.cpp 内にある下記のコードが、各頂点の座標と頂点法線ベクトルです。
三角形576のように独立した平面の場合には、面法線ベクトルは 頂点法線ベクトルと同じになります。
static VertexPositionColor verticesPosNrm[24];
// 頂点の座標
verticesPosNrm[ 0].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[ 1].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[ 2].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[ 3].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[ 4].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[ 5].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[ 6].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[ 7].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
verticesPosNrm[ 8].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[ 9].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[10].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[11].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[12].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[13].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[14].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[15].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
verticesPosNrm[16].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[17].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[18].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[19].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[20].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[21].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[22].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[23].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
// 頂点法線ベクトル
verticesPosNrm[ 0].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 1].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 2].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 3].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 4].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 5].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 6].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 7].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 8].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[ 9].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[10].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[11].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[12].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[13].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[14].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[15].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[16].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[17].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[18].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[19].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[20].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[21].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[22].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[23].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
《その431》シェーダ(平行光線の場合)(1)
シェーダ(平行光線の場合)
ウィキペディアによると、「シェーダー(英: shader)とは、3次元コンピュータグラフィックスにおいて、シェーディング(陰影処理)を行うコンピュータプログラムのこと」だそうです。
本ブログの《427》~《429》では立方体を描画しましたが、今回は立方体に平行光線による陰影処理をしてみたいと思います。
次の画像は、陰影処理した立方体です。
回転角度が 0度のとき
回転角度が 30度のとき
回転角度が 45度のとき
回転角度が 60度のとき
下記はプログラムですが、分量が多いので今回と次回は Sample3DSceneRenderer.cpp のコードについて のみです。
※次の3つのプログラムに関しては、《433》以降になります。
ShaderStructures.h
SamplePixelShader.hlsl
SampleVertexShader.hlsl
以下は、Sample3DSceneRenderer.cpp のコードです。
※コードは長いですが、陰影処理に関する箇所以外はデフォルトのサンプルアプリのままです。
(枠から右にはみだした部分のコードを見る際は、最下部のスクロールバーをご利用ください。)
#include "pch.h"
#include "Sample3DSceneRenderer.h"
#include "..\Common\DirectXHelper.h"
using namespace App20;
using namespace DirectX;
using namespace Windows::Foundation;
// ファイルから頂点とピクセルシェーダを読み込み、キューブのジオメトリをインスタンス化します。
Sample3DSceneRenderer::Sample3DSceneRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
m_loadingComplete(false),
m_degreesPerSecond(45),
m_indexCount(0),
m_tracking(false),
m_deviceResources(deviceResources)
{
CreateDeviceDependentResources();
CreateWindowSizeDependentResources();
}
// ウィンドウのサイズが変更されたときに、ビューのパラメータを初期化します。
void Sample3DSceneRenderer::CreateWindowSizeDependentResources()
{
Size outputSize = m_deviceResources->GetOutputSize();
float aspectRatio = outputSize.Width / outputSize.Height;
float fovAngleY = 70.0f * XM_PI / 180.0f;
// これは、アプリケーションが縦向きビューまたはスナップビュー内にあるときに行うことのできる変更の簡単な例です。
if (aspectRatio < 1.0f)
{
fovAngleY *= 2.0f;
}
// OrientationTransform3D マトリックスは、シーンの方向を表示方向と正しく一致させるため、ここで事後乗算されます。
// この事後乗算ステップは、スワップチェーンのターゲットビットマップに対して行われるすべての描画呼び出しで実行する必要があります。
// 他のターゲットに対する呼び出しでは、適用する必要はありません。
// このサンプルでは、行優先のマトリックスを使用した右辺座標系を使用しています。
XMMATRIX perspectiveMatrix = XMMatrixPerspectiveFovRH(
fovAngleY,
aspectRatio,
0.01f,
100.0f
);
XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();
XMMATRIX orientationMatrix = XMLoadFloat4x4(&orientation);
XMStoreFloat4x4(
&m_constantBufferData.projection,
XMMatrixTranspose(perspectiveMatrix * orientationMatrix)
);
// 視点は (0,0.7,1.5) の位置にあり、y軸に沿って上方向のポイント (0,-0.1,0) を見ています。
static const XMVECTORF32 eye = { 0.0f, 0.7f, 1.5f, 0.0f };
static const XMVECTORF32 at = { 0.0f, -0.1f, 0.0f, 0.0f };
static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };
XMStoreFloat4x4(&m_constantBufferData.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
}
// フレームごとに 1回呼び出し、キューブを回転させてから、モデルおよびビューのマトリックスを計算します。
void Sample3DSceneRenderer::Update(DX::StepTimer const& timer)
{
if (!m_tracking)
{
// 度をラジアンに変換し、秒を回転角度に変換します。
float radiansPerSecond = XMConvertToRadians(m_degreesPerSecond);
double totalRotation = timer.GetTotalSeconds() * radiansPerSecond;
float radians = static_cast<float>(fmod(totalRotation, XM_2PI));
Rotate(radians);
}
}
// 3Dキューブモデルを、ラジアン単位で設定された大きさだけ回転させます。
void Sample3DSceneRenderer::Rotate(float radians)
{
// 更新されたモデルマトリックスをシェーダに渡す準備をします。
XMStoreFloat4x4(&m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(radians)));
}
void Sample3DSceneRenderer::StartTracking()
{
m_tracking = true;
}
// 追跡時に、出力画面の幅方向を基準としてポインタの位置を追跡することにより、3Dキューブを Y軸に沿って回転させることができます。
void Sample3DSceneRenderer::TrackingUpdate(float positionX)
{
if (m_tracking)
{
float radians = XM_2PI * 2.0f * positionX / m_deviceResources->GetOutputSize().Width;
Rotate(radians);
}
}
void Sample3DSceneRenderer::StopTracking()
{
m_tracking = false;
}
// 頂点とピクセルシェーダを使用して、1つのフレームを描画します。
void Sample3DSceneRenderer::Render()
{
// 読み込みは非同期です。読み込みが完了した後にのみ描画してください。
if (!m_loadingComplete)
{
return;
}
auto context = m_deviceResources->GetD3DDeviceContext();
// 定数バッファを準備して、グラフィックスデバイスに送信します。
context->UpdateSubresource1(
m_constantBuffer.Get(),
0,
NULL,
&m_constantBufferData,
0,
0,
0
);
// 各頂点は、VertexPositionColor構造体の 1つのインスタンスです。
UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;
context->IASetVertexBuffers(
0,
1,
m_vertexBuffer.GetAddressOf(),
&stride,
&offset
);
context->IASetIndexBuffer(
m_indexBuffer.Get(),
DXGI_FORMAT_R16_UINT, // 各インデックスは、1つの 16ビット符号なし整数(short) です。
0
);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->IASetInputLayout(m_inputLayout.Get());
// 頂点シェーダをアタッチします。
context->VSSetShader(
m_vertexShader.Get(),
nullptr,
0
);
// 定数バッファをグラフィックスデバイスに送信します。
context->VSSetConstantBuffers1(
0,
1,
m_constantBuffer.GetAddressOf(),
nullptr,
nullptr
);
// ピクセルシェーダをアタッチします。
context->PSSetShader(
m_pixelShader.Get(),
nullptr,
0
);
// オブジェクトを描画します。
context->DrawIndexed(
m_indexCount,
0,
0
);
}
void Sample3DSceneRenderer::CreateDeviceDependentResources()
{
// シェーダを非同期で読み込みます。
auto loadVSTask = DX::ReadDataAsync(L"SampleVertexShader.cso");
auto loadPSTask = DX::ReadDataAsync(L"SamplePixelShader.cso");
// 頂点シェーダファイルを読み込んだ後、シェーダと入力レイアウトを作成します。
auto createVSTask = loadVSTask.then([this](const std::vector<byte>& fileData) {
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateVertexShader(
&fileData[0],
fileData.size(),
nullptr,
&m_vertexShader
)
);
static const D3D11_INPUT_ELEMENT_DESC vertexDesc [] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateInputLayout(
vertexDesc,
ARRAYSIZE(vertexDesc),
&fileData[0],
fileData.size(),
&m_inputLayout
)
);
});
// ピクセルシェーダファイルを読み込んだ後、シェーダと定数バッファを作成します。
auto createPSTask = loadPSTask.then([this](const std::vector<byte>& fileData) {
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreatePixelShader(
&fileData[0],
fileData.size(),
nullptr,
&m_pixelShader
)
);
CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer) , D3D11_BIND_CONSTANT_BUFFER);
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateBuffer(
&constantBufferDesc,
nullptr,
&m_constantBuffer
)
);
});
// 両方のシェーダの読み込みが完了したら、メッシュを作成します。
auto createCubeTask = (createPSTask && createVSTask).then([this] () {
// メッシュの頂点座標と頂点法線ベクトルを読み込みます。
static VertexPositionColor verticesPosNrm[24];
verticesPosNrm[ 0].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[ 1].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[ 2].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[ 3].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[ 4].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[ 5].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[ 6].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[ 7].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
verticesPosNrm[ 8].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[ 9].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[10].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[11].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[12].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[13].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[14].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[15].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
verticesPosNrm[16].pos = XMFLOAT3(-0.5f, -0.5f, -0.5f);
verticesPosNrm[17].pos = XMFLOAT3(-0.5f, -0.5f, 0.5f);
verticesPosNrm[18].pos = XMFLOAT3(-0.5f, 0.5f, -0.5f);
verticesPosNrm[19].pos = XMFLOAT3(-0.5f, 0.5f, 0.5f);
verticesPosNrm[20].pos = XMFLOAT3( 0.5f, -0.5f, -0.5f);
verticesPosNrm[21].pos = XMFLOAT3( 0.5f, -0.5f, 0.5f);
verticesPosNrm[22].pos = XMFLOAT3( 0.5f, 0.5f, -0.5f);
verticesPosNrm[23].pos = XMFLOAT3( 0.5f, 0.5f, 0.5f);
verticesPosNrm[ 0].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 1].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 2].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 3].nrm = XMFLOAT3(-1.0f, 0.0f, 0.0f);
verticesPosNrm[ 4].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 5].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 6].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 7].nrm = XMFLOAT3( 1.0f, 0.0f, 0.0f);
verticesPosNrm[ 8].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[ 9].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[10].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[11].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[12].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[13].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[14].nrm = XMFLOAT3( 0.0f, 0.0f, -1.0f);
verticesPosNrm[15].nrm = XMFLOAT3( 0.0f, 0.0f, 1.0f);
verticesPosNrm[16].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[17].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[18].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[19].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[20].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[21].nrm = XMFLOAT3( 0.0f, -1.0f, 0.0f);
verticesPosNrm[22].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
verticesPosNrm[23].nrm = XMFLOAT3( 0.0f, 1.0f, 0.0f);
D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
vertexBufferData.pSysMem = verticesPosNrm;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(verticesPosNrm), D3D11_BIND_VERTEX_BUFFER);
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateBuffer(
&vertexBufferDesc,
&vertexBufferData,
&m_vertexBuffer
)
);
// メッシュのインデックスを読み込みます。インデックスの 3つ 1組の値のそれぞれは、画面上に描画される三角形を表します。
// たとえば、0,2,1 とは、頂点バッファのインデックス 0, 2, 1 にある頂点が、このメッシュの最初の三角形を構成することを意味します。
static const unsigned short cubeIndices [] =
{
0,2,1, // -x
1,2,3,
4,5,6, // +x
5,7,6,
16,17,21, // -y
16,21,20,
18,22,23, // +y
18,23,19,
8,12,10, // -z
10,12,14,
9,11,15, // +z
9,15,13,
};
m_indexCount = ARRAYSIZE(cubeIndices);
D3D11_SUBRESOURCE_DATA indexBufferData = {0};
indexBufferData.pSysMem = cubeIndices;
indexBufferData.SysMemPitch = 0;
indexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER);
DX::ThrowIfFailed(
m_deviceResources->GetD3DDevice()->CreateBuffer(
&indexBufferDesc,
&indexBufferData,
&m_indexBuffer
)
);
});
// キューブが読み込まれたら、オブジェクトを描画する準備が完了します。
createCubeTask.then([this] () {
m_loadingComplete = true;
});
}
void Sample3DSceneRenderer::ReleaseDeviceDependentResources()
{
m_loadingComplete = false;
m_vertexShader.Reset();
m_inputLayout.Reset();
m_pixelShader.Reset();
m_constantBuffer.Reset();
m_vertexBuffer.Reset();
m_indexBuffer.Reset();
}