https://youtu.be/98rSC9a0LvY
http://www.rastertek.com/tutdx11.html
基本的にこのサイトさんを中心にDirectX11を触ってた。
■メモリ
ゲームオブジェクト用のメモリ領域を保持管理するために、メモリプールクラスを作った。
メモリ断片化を防ぐ狙いだけど、今のところそこまで長時間使用したことがないので効果のほどはわからない。
patriotDark、ring^-27、LUXでもメモリ管理はしていたけど、今回ちょっと仕組みをいじくって新調した。
メモリを返却時に前後のメモリ使用状況をみて結合する。
■パックファイル
複数ファイルを一ファイルにまとめる仕組み。
ZIPファイル構造をまねして、ファイル一覧情報を終端に置いた。
各ファイル内容をメモリに保持することなく記述できるし、読み込み時のファイル一覧取得もパックファイル終端だけを読めば取得できるので負担が小さめ。
ファイル書き込み時に圧縮して保存するオプションをついでにつけてみたけど、今のところ使ってない。
■mesh
http://www.rastertek.com/dx11tut08.html
をそのまま使って読み込みを行い、バイナリにして出力するツールを作成、メインプログラムでバイナリを読み込んで表示までもっていっている。
.objはBlenderでも出力できるので制作環境を変えずに済んでいる。
■ブラーシェーダ
よくある2パスつかって、コンピュートシェーダで縦横ぼかすやつ。
https://sites.google.com/site/monshonosuana/directxno-hanashi-1/directx-109
もんしょさんところにあった各ピクセルをgroupsharedメモリに一旦ためるプログラムを拝借した。
参照するテクスチャと書き込むテクスチャの大きさが違っても動くようにしている。
ピクセル位置計算がすごい雑だが、まあぼかすからいいよねって感じに雑にしている。
ブラーシェーダはDOFとブルームに使用。
DOF用のブラーはメイン画面の半分のサイズで速度とボケ具合を稼いでいる。
#define BLOCK_SIZE (16)
#define GROUP_THREADS (BLOCK_SIZE*BLOCK_SIZE)
cbuffer WeightBufferType : register( c0 )
{
float gWeight0;
float gWeight1;
float gWeight2;
float gWeight3;
float gWeight4;
int gIsHorizon;
float2 gSrcImageSize;
float2 gDestImageSize;
float padding;
float padding1;
};
Texture2D gColorTexture : register( t0 );
RWTexture2D<float4> rwTexture : register( u0 );
groupshared float4 shColor[BLOCK_SIZE * BLOCK_SIZE * 4];
uint2 GetInputPixelIndex( int2 basePos, int2 pos )
{
float2 uv = float2( basePos + pos ) / gDestImageSize;
uv = clamp( uv * gSrcImageSize, 0, gSrcImageSize-1 );
return uint2( uv );
}
[numthreads( BLOCK_SIZE, BLOCK_SIZE, 1 )]
void main(
uint3 DTid : SV_DispatchThreadID,
uint3 GTid : SV_GroupThreadID,
uint3 Gid : SV_GroupID )
{
int2 basePos = Gid.xy * BLOCK_SIZE - (BLOCK_SIZE / 2);
uint indexX = GTid.y * ( BLOCK_SIZE * 2 ) + GTid.x;
uint indexY = ( GTid.y + BLOCK_SIZE ) * ( BLOCK_SIZE * 2 ) + GTid.x;
shColor[indexX] = gColorTexture[GetInputPixelIndex( basePos, GTid.xy )];
shColor[indexX + BLOCK_SIZE] = gColorTexture[GetInputPixelIndex( basePos, GTid.xy + uint2( BLOCK_SIZE, 0 ) )];
shColor[indexY] = gColorTexture[GetInputPixelIndex( basePos, GTid.xy + uint2( 0, BLOCK_SIZE ) )];
shColor[indexY + BLOCK_SIZE] = gColorTexture[GetInputPixelIndex( basePos, GTid.xy + uint2( BLOCK_SIZE, BLOCK_SIZE ) )];
GroupMemoryBarrierWithGroupSync();
if ( gIsHorizon )
{
// 水平ブラー計算してテクスチャにいったん書込み
int2 pos = GTid.xy + ( BLOCK_SIZE / 2 );
float4 color = shColor[pos.y * ( BLOCK_SIZE * 2 ) + pos.x] * gWeight0;
float w[5];
w[0] = 0;
w[1] = gWeight1;
w[2] = gWeight2;
w[3] = gWeight3;
w[4] = gWeight4;
for ( int dist = 1; dist <= 4; dist++ )
{
color += shColor[pos.y * ( BLOCK_SIZE * 2 ) + pos.x + dist] * w[dist];
color += shColor[pos.y * ( BLOCK_SIZE * 2 ) + pos.x - dist] * w[dist];
}
rwTexture[DTid.xy] = color;
}
else
{
int2 pos = GTid.xy + ( BLOCK_SIZE / 2 );
float4 color = shColor[pos.y * ( BLOCK_SIZE * 2 ) + pos.x] * gWeight0;
float w[5];
w[0] = 0;
w[1] = gWeight1;
w[2] = gWeight2;
w[3] = gWeight3;
w[4] = gWeight4;
// 縦ブラー計算してテクスチャに書き込み
for ( int dist = 1; dist <= 4; dist++ )
{
color += shColor[( pos.y + dist ) * ( BLOCK_SIZE * 2 ) + pos.x] * w[dist];
color += shColor[( pos.y - dist ) * ( BLOCK_SIZE * 2 ) + pos.x] * w[dist];
}
rwTexture[DTid.xy] = color;
}
}
[2022/03/31]GetInputPixelIndex()がうまく動作してないのをサイレント修正。■ブライトシェーダ
一定の明るさ以上の部分を抜き出す。明るさ計算にグレースケールにして計算して、一定以下ならアルファを0にしている。
ブルームさせたい部位があるオブジェクトのために、マルチレンダーダーゲット可能なようメイン画面と同じサイズのテクスチャにしている。
float4 main( PixelInputType input ) : SV_TARGET
{
float4 color = shaderTexture.Sample( ss, input.tex ) * input.color;
float brightness = dot( color.rgb, float3( 0.2126, 0.7152, 0.0722 ) );
float a = saturate( 1 - step( brightness, bright ) );
return float4( color.xyz, a );
}
■ブルームシェーダ
ブライトシェーダで抜き出した明るい部分をテクスチャ縮小しながら3枚作成して、それぞれスクリーン合成した。
最初は4枚合成してたけど、あまりにも重いので3枚に減らした。
1枚目がメイン画面の1/4、2枚目が1/8、3枚目が1/16のサイズ。
Texture2D gColorTexture1 : register( t0 );
Texture2D gColorTexture2 : register( t1 );
Texture2D gColorTexture3 : register( t2 );
//Texture2D gColorTexture4 : register( t3 );
SamplerState ss : register( s0 );
float4 main( PixelInputType input ) : SV_TARGET
{
float4 c1 = gColorTexture1.Sample( ss, input.tex ) * gBloomWeight1;
float4 c2 = gColorTexture2.Sample( ss, input.tex ) * gBloomWeight2;
float4 c3 = gColorTexture3.Sample( ss, input.tex ) * gBloomWeight3;
//float4 c4 = gColorTexture4.Sample( ss, input.tex ) * gBloomWeight4;
float4 c = c1 + c2 - c1*c2;
c = c + c3 - c*c3;
//c = c + c4 - c*c4;
c = saturate( c );
return c * gBloomMasterWeight;
}
■ポストエフェクト用の頂点シェーダ
https://hikita12312.hatenablog.com/entry/2018/01/25/230502
を参考に、画面全体を書き換える用の頂点シェーダを作成、使用している。
ポリゴンの表裏が違うのかうまく描画できなかったのでちょっといじっている。
PixelInputType main( uint index : SV_VertexID )
{
PixelInputType output;
float2 vID = float2( index % 2, index / 2 ); //< (0,0)(1,0)(0,1)
float2 pos = float2( 4, -4 ) * vID.xy + float2( -1, 1 ); //< (-1,1)(-3,1)(-1,3)
output.pos = float4( pos, 0, 1 );
output.tex = float2( 2, 2 ) * vID.xy; //< (0,0)(2,0)(0,2)
return output;
}
あと、画面回転に対応するために、回転可能なのも使っている。大した計算コストでもないだろうから下だけでいいかもしれない。
PixelInputType main( uint index : SV_VertexID )
{
PixelInputType output;
float2 vID = float2( index % 2, index / 2 ); //< (0,0)(1,0)(0,1)
float2 pos = float2( 4, -4 ) * vID.xy + float2( -1, 1 ); //< (-1,1)(3,1)(-1,-3)
float2 rotatePos;
rotatePos.x = cos( gRotateAngle ) * pos.x - sin( gRotateAngle ) * pos.y;
rotatePos.y = sin( gRotateAngle ) *pos.x + cos( gRotateAngle ) * pos.y;
output.pos = float4( rotatePos, 0, 1 );
output.tex = float2( 2, 2 ) * vID.xy; //< (0,0)(2,0)(0,2)
return output;
}
■CPUパーティクル
世間ではGPUパーティクルが主流だけど。
頂点を生成して頂点シェーダ→ジオメトリシェーダ→ピクセルシェーダで描画する方法を使っている。
下は、一頂点から2ポリゴン生成するジオメトリシェーダ。
ビルボードとして表示するのでカメラ方向へ向きを変える行列を渡している。
[maxvertexcount(6)]
void main( point GeometryInputType input[1], inout TriangleStream<PixelInputType> outStream )
{
float billboardSize = input[0].pos.w;
float3 billboardPos[6];
billboardPos[0] = mul( float3( -0.5f, -0.5f, 0 ), ( float3x3 )gBillboard ).xyz * billboardSize;
billboardPos[1] = mul( float3( -0.5f, 0.5f, 0 ), ( float3x3 )gBillboard ).xyz * billboardSize;
billboardPos[2] = mul( float3( 0.5f, -0.5f, 0 ), ( float3x3 )gBillboard ).xyz * billboardSize;
billboardPos[3] = billboardPos[2];
billboardPos[4] = billboardPos[1];
billboardPos[5] = mul( float3( 0.5f, 0.5f, 0 ), ( float3x3 )gBillboard ).xyz * billboardSize;
float2 uv[6];
uv[0] = float2( 0, 0 );
uv[1] = float2( 0, 1 );
uv[2] = float2( 1, 0 );
uv[3] = float2( 1, 0 );
uv[4] = float2( 0, 1 );
uv[5] = float2( 1, 1 );
{
int j;
for ( j=0; j<3; j++)
{
PixelInputType output;
output.pos = float4( input[0].pos.xyz, 1 );
output.pos.xyz += billboardPos[j];
output.pos = mul( output.pos, gWorld );
output.pos = mul( output.pos, gView );
output.pos = mul( output.pos, gProjection );
output.color = input[0].color;
output.tex = uv[j];
outStream.Append( output );
}
outStream.RestartStrip();
for ( j = 3; j<6; j++ )
{
PixelInputType output;
output.pos = float4( input[0].pos.xyz, 1 );
output.pos.xyz += billboardPos[j];
output.pos = mul( output.pos, gWorld );
output.pos = mul( output.pos, gView );
output.pos = mul( output.pos, gProjection );
output.color = input[0].color;
output.tex = uv[j];
outStream.Append( output );
}
outStream.RestartStrip();
}
}
■影深度値見て影描画する普通の奴。
512x512のテクスチャサイズにしている。大きすぎるかもしれない。
アラクネがアホほど出て悩んだけど、影用の深度値を記録するときにモデルの裏表を逆にする手法でかなり改善できた。
影の描画にすこしランダムを使ってみている。
ランダムには
http://neareal.net/index.php?ComputerGraphics%2FHLSL%2FCommon%2FGenerateRandomNoiseInPixelShader を使っている。
float GetRandomNumber( float2 texCoord, int Seed )
{
return frac( sin( dot( texCoord.xy, float2( 12.9898, 78.233 ) ) + Seed ) * 43758.5453 );
}
float CalcShadowFactor( float4 shadowPosH )
{
shadowPosH.xyz /= shadowPosH.w;
float shadowBias = 0.001f;
float depth = shadowPosH.z;
float percentLit = 0.0f;
const int NUM_OF_SAMPLING = 2; //< サンプリング数
const float DIST_OF_SAMPLING = 2; //< サンプリング最大距離
percentLit += gShadowTexture.SampleCmpLevelZero( samShadow, shadowPosH.xy, depth - shadowBias ).r;
for ( int i = 0; i < NUM_OF_SAMPLING; ++i )
{
float rndX = ( GetRandomNumber( shadowPosH.xy, i ) - 0.5f ) * ( (1 / gShadowTexSize.x) * DIST_OF_SAMPLING );
float rndY = ( GetRandomNumber( shadowPosH.xy, i + 29 ) - 0.5f ) * ( ( 1 / gShadowTexSize.y ) * DIST_OF_SAMPLING );
percentLit += gShadowTexture.SampleCmpLevelZero( samShadow, shadowPosH.xy + float2(rndX, rndY), depth - shadowBias ).r;
}
return percentLit / ( NUM_OF_SAMPLING + 1 );
}
■DOF奥行情報の取得にZバッファを渡して、そこからビュー空間のZにしている。
https://stackoverflow.com/questions/32227283/getting-world-position-from-depth-buffer-value
を参考にした。
プロジェクション行列の逆行列を使ってworld*view*projからworld*viewの情報にする。
float3 depthToViewPos( float2 tex )
{
float4 clipSpacePos;
clipSpacePos.x = tex.x * 2 - 1; //< 0~1 -> -1~1
clipSpacePos.y = -tex.y * 2 + 1; //< 0~1 -> -1~1ただし上が1になって下が-1になる
clipSpacePos.z = gDepthTexture.Sample( ss, tex ).r;
clipSpacePos.w = 1;
float4 pos = mul( clipSpacePos, gInvProj );
pos /= pos.w;
return pos.xyz;
}
あとは普通の画像とぼかした画像からいい感じに距離に応じてボケてみえるようにするだけだが、手前がぼけておくがくっきりしているの対策に、上下左右の奥行情報をさらに取得してボケ範囲を広くしてみている。もうちょいましな方法がある気がするけど。
float4 main( PixelInputType input ) : SV_TARGET
{
const float SAMPLING_DIST = 4;
float z = depthToViewPos( input.tex ).z;
float z1 = depthToViewPos( input.tex + float2( SAMPLING_DIST / gDestImageWidth, 0 ) ).z;
float z2 = depthToViewPos( input.tex + float2( -SAMPLING_DIST / gDestImageWidth, 0 ) ).z;
float z3 = depthToViewPos( input.tex + float2( 0, SAMPLING_DIST / gDestImageHeight ) ).z;
float z4 = depthToViewPos( input.tex + float2( 0, -SAMPLING_DIST / gDestImageHeight ) ).z;
float mostNear = min( z4, min( z3, min( z1, z2 ) ) );
z = z < gFocusZ ? ( z + mostNear ) / 2 : z;
float dist = abs( z - gFocusZ );
float blend = saturate( ( dist - gStartBlurRange ) / ( gEndBlurRange - gStartBlurRange ) );
float3 colorA = gColorTexture.Sample( ss, input.tex ).rgb;
float3 colorB = gBlurTexture.Sample( ss, input.tex ).rgb;
return float4( lerp( colorA, colorB, blend ), 1 );
}
■FXAA
http://dolpetticoat.blog.fc2.com/blog-entry-103.html
のサイトさんのをそのまま使っている。
■フォンシェーダ
https://www.cnblogs.com/X-Jun/p/9028764.html
のBasic_PSわりとそのまま使っている。
ただそのまま使うとうまく動かないところがあったのでちょっといじっている。まず頂点シェーダの部分、
output.normalW = mul( input.normal, ( float3x3 )input.invWorld );
output.tangentW = mul( input.tangent, ( float3x3 )world );に、あとピクセルシェーダのDirectLightの部分、
float3 lightVec = -L.direction;
float3 lightVec = L.direction;にした。なんかしら渡すパラメータが間違えてるんだろうたぶん。
PixelInputType main( VertexInputType input )
{
PixelInputType output;
float4x4 world = mul( input.world, gWorld );
output.pos = mul( float4(input.pos, 1), world );
output.posW = output.pos;
output.pos = mul( output.pos, gView );
output.pos = mul( output.pos, gProjection );
output.tex = input.tex;
output.normalW = mul( input.normal, (float3x3)world );
//output.normalW = mul( input.normal, ( float3x3 )input.invWorld );
output.tangentW = mul( input.tangent, ( float3x3 )world );
output.binormalW = mul( input.binormal, ( float3x3 )world );
output.normalW = normalize( output.normalW );
output.tangentW = normalize( output.tangentW );
output.binormalW = normalize( output.binormalW );
output.shadowPosH = mul( output.posW, gShadowTransform );
return output;
}
void ComputeDirectionalLight( Material mat, DirectionalLight L,
float3 normal, float3 toEye,
out float4 ambient,
out float4 diffuse,
out float4 spec )
{
// 初期化
ambient = float4( 0.0f, 0.0f, 0.0f, 0.0f );
diffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f );
spec = float4( 0.0f, 0.0f, 0.0f, 0.0f );
//float3 lightVec = -L.direction;
float3 lightVec = L.direction;
ambient = mat.ambient * L.ambient;
float diffuseFactor = dot( lightVec, normal );
[flatten]
if ( diffuseFactor > 0.0f )
{
float3 v = reflect( -lightVec, normal );
float specFactor = pow( max( dot( v, toEye ), 0.0f ), mat.specular.w );
diffuse = diffuseFactor * mat.diffuse * L.diffuse;
spec = saturate(specFactor * mat.specular * L.specular);
}
}
点光源が範囲くっきり照らされてるようだったので遠くほどひからなくなる処理を入れている。
void ComputePointLight( Material mat, PointLight L, float3 pos, float3 normal, float3 toEye,
out float4 ambient, out float4 diffuse, out float4 spec )
{
ambient = float4( 0.0f, 0.0f, 0.0f, 0.0f );
diffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f );
spec = float4( 0.0f, 0.0f, 0.0f, 0.0f );
float3 lightVec = L.position - pos;
float d = length( lightVec );
if ( d > L.range )
return;
lightVec /= d;
ambient = mat.ambient * L.ambient * ( 1 - d / L.range );;
// 効果範囲で徐々に減光するようにする
float diffuseFactor = dot( lightVec, normal );
[flatten]
if ( diffuseFactor > 0.0f )
{
float3 v = reflect( -lightVec, normal );
float specFactor = pow( max( dot( v, toEye ), 0.0f ), mat.specular.w );
diffuse = diffuseFactor * mat.diffuse * L.diffuse;
spec = specFactor * mat.specular * L.specular;
}
float att = 1.0f / dot( L.att, float3( 1.0f, d, d * d ) );
// 効果範囲で徐々に減光するようにする
att *= ( 1 - d / L.range );
diffuse *= att;
spec *= att;
}
あとフォグ欲しかったのでつけ足してみたりしている。
float fogLerp = saturate( ( distToEye - gFogStart ) / ( gFogEnd - gFogStart ) );
litColor = lerp( litColor, gFogColor, fogLerp );
意外に描画処理が重いので遠くのノーマルマップは無視するとかの軽量化の必要を感じている。■歪みエフェクト
歪みエフェクト?用のシェーダ。
まるで何を参考にしていいのかわからんので、バンプマップの処理をもってきている。
後、見た目いい感じにするべく-1をかけて反転してみてみたりしている。
テクスチャはBlenderのNormalベイクで作成。
float4 main( PixelInputType input ) : SV_TARGET
{
float4 normalMap = normalTexture.Sample( ss, input.tex );
clip( normalMap.a - 0.1f );
float2 refractTexCoord;
refractTexCoord.x = input.refractionPos.x / input.refractionPos.w / 2.0f + 0.5f;
refractTexCoord.y = -input.refractionPos.y / input.refractionPos.w / 2.0f + 0.5f;
float3 normal = ( normalMap.xyz * 2.f ) - 1.0f;
normal *= -1;
refractTexCoord = refractTexCoord + ( normal.xy * gRefractionScale );
float4 color = colorTexture.Sample( ss, refractTexCoord );
return float4( color.rgb, 1 ) * input.color;
}
■スカイキューブ
10年ぐらい前に書いたスカイキューブ用のシェーダをかねがねそのまま持ってきてた。
データはDirectXSDKからそのまま拝借した。
以下、頂点シェーダとピクセルシェーダ。
cbuffer MatrixBuffer : register( b0 )
{
float4x4 world;
float4x4 view;
float4x4 projection;
};
PixelInputType main( VertexInputType input )
{
PixelInputType output;
float4x4 matNoTransView = {
view[0],
view[1],
view[2],
float4( 0.f, 0.f, 0.f, 1.f )
};
output.pos = mul( float4( input.pos, 1 ), mul( matNoTransView, projection ) );
output.tex = input.pos;
return output;
}
float4 main( PixelInputType input ) : SV_TARGET
{
return cubeTexture.Sample( SampleType, input.tex );
}
■インスタンシングスプライト
敵弾等をインスタンシングで描画する。
メッシュにサイズが1x1、uvも0->1のものを使用してインスタンシング用データで加工して描画している。
インスタンシング用データには
位置大きさ回転:float4x4 world
頂点カラー:float4 colorInstance
uv: uvInstance, uv2Instance
の情報を持たせてテクスチャのス来たところを好きな大きさ回転角度で描画できるようにしてみている。
一番難解なのがInputLayoutをどう書けばいいのかよくわからないところだった。
D3D11_INPUT_ELEMENT_DESC vertexDesc[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "world", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
{ "world", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
{ "world", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
{ "world", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
{ "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
{ "TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
};
cbuffer MatrixBuffer : register( b0 )
{
float4x4 g_world;
float4x4 g_view;
float4x4 g_projection;
};
struct VertexInputType
{
float3 pos : POSITION0;
float2 tex : TEXCOORD0;
float4x4 world : world;
float4 colorInstance : COLOR0;
float2 uvInstance : TEXCOORD1; //< uv始点
float2 uv2Instance : TEXCOORD2; //< uv終点
};
struct PixelInputType
{
float4 pos : SV_POSITION;
float4 color : COLOR0;
float2 tex : TEXCOORD0;
};
PixelInputType main( VertexInputType input )
{
PixelInputType output;
float4x4 mat = mul( g_world, input.world );
output.pos = mul( float4( input.pos, 1 ), mat );
output.pos = mul( output.pos, g_view );
output.pos = mul( output.pos, g_projection );
output.color = input.colorInstance;
float texWidth = input.uv2Instance.x - input.uvInstance.x;
float texHeight = input.uv2Instance.y - input.uvInstance.y;
output.tex.x = input.uvInstance.x + texWidth * input.tex.x;
output.tex.y = input.uvInstance.y + texHeight * input.tex.y;
return output;
}
■SamplingAnistropic
割と簡単に使用できた。角度が急な面の描写がしっかりした感じになる。
検索すると重いとでてくるが自分の環境と状況では特に処理負担に変化は見られなかったが、一応オンオフできるようにしている。
■音まわり
DirectX11にはDirectSoundがないのでXAudio2で作り直す必要があった。
曲再生で停止してすぐ再生したらFlushSourceBuffers()してても前のバッファが残ってしまう問題が発生したので、
あきらめて曲の停止をしたらDestroyVoice()して、再生時にいちいちCreateSourceVoice()するようにして解決した。
staticLibraryが好きなのでoggVorbisを最新にしたりちまちまとした作業が必要だった。
■xinput
いままではWinAPIでキーやゲームパッドの入力をみていたが、今回はDirectXでチェックしてみてみた。
ゲームパッド周りはXInputの便利関数が用意されているので、WinAPIよりもさらに簡単に入力を取得できる。
class XInputWrapper
{
public:
// 使用するコントローラの番号を指定する0~3
XInputWrapper( int controllerNo )
: m_controllerNo( controllerNo )
{
ZeroMemory( &m_inputState, sizeof( m_inputState ) );
}
void update()
{
XInputGetState( m_controllerNo, &m_inputState );
}
// -1 ~ 1
// 閾値以下なら触っていないようにする処理が別に必要だが、とりあえずデータの取り方だけ見ておく
float getStickLeftX() const { return static_cast<float>( m_inputState.Gamepad.sThumbLX ) / 32767; }
float getStickLeftY() const { return static_cast<float>( m_inputState.Gamepad.sThumbLY ) / 32767; }
float getStickRightX() const { return static_cast<float>( m_inputState.Gamepad.sThumbRX ) / 32767; }
float getStickRightY() const { return static_cast<float>( m_inputState.Gamepad.sThumbRY ) / 32767; }
float getTriggerLeft() const { return static_cast<float>( m_inputState.Gamepad.bLeftTrigger ) / 255; }
float getTriggerRight() const { return static_cast<float>( m_inputState.Gamepad.bRightTrigger ) / 255; }
bool getUp() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP; }
bool getDown() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN; }
bool getLeft() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT; }
bool getRight() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT; }
bool getStartButton() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_START; }
bool getBackButton() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_BACK; }
// スティック押し込み
bool getThubLeft() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB; }
bool getThubRight() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB; }
// LRボタン
bool getShoulderLeft() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER; }
bool getShoulderRight() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER; }
bool getButtonA() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_A; }
bool getButtonB() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_B; }
bool getButtonX() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_X; }
bool getButtonY() const { return m_inputState.Gamepad.wButtons & XINPUT_GAMEPAD_Y; }
// 設定した振動値にする
void setVibration( WORD leftMoterSpeed, WORD rightMoterSpeed )
{
XINPUT_VIBRATION vibration;
vibration.wLeftMotorSpeed = leftMoterSpeed;
vibration.wRightMotorSpeed = rightMoterSpeed;
XInputSetState( m_controllerNo, &vibration );
}
// 使用するコントローラ番号を設定
void changeControllerNo( int no )
{
// 範囲外の数値だったら0にでもしておく
if ( no > 3 || no < 0 ) no = 0;
m_controllerNo = no;
}
private:
int m_controllerNo; //< 0~3
XINPUT_STATE m_inputState;
};
ただ今家にゲームコントローラがないので動作チェックは一切できていない。うごくのこれ?■directInput_key
キーボード入力はDirrectInputを使った。
ちょっとひっかかったところはDirectInputKeyboardの配列がVKではなく独自のDIKを使用する部分だった。Windowsになれすぎている。
■そのほかよく使う処理とか、調べたときよくわからなかった記述方法とか。
■行列計算
static DirectX::XMMATRIX multiply( const DirectX::XMMATRIX& pos, const DirectX::XMMATRIX& scale, const DirectX::XMMATRIX& rotate )
{
return DirectX::XMMatrixMultiply( DirectX::XMMatrixMultiply( scale, rotate ), pos );
}
scale * posみたいに普通に記述も可能だけど計算順序が安定しないので素直に乗算用の関数を使用するようにしている。■ビルボード行列
static DirectX::XMMATRIX calcBillboardMat( const DirectX::XMFLOAT4X4& view )
{
DirectX::XMMATRIX viewMat = DirectX::XMLoadFloat4x4( &view );
viewMat = DirectX::XMMatrixInverse( NULL, viewMat );
DirectX::XMFLOAT4X4 v;
DirectX::XMStoreFloat4x4( &v, viewMat );
v._41 = 0;
v._42 = 0;
v._43 = 0;
return DirectX::XMLoadFloat4x4( &v );
}
カメラの逆行列から位置情報を削除したもの。これをビルボード表示したいオブジェクトのワールド行列に混ぜて使う。
■シェーダ用コンスタントバッファ作成
static HRESULT createConstBuffer( ID3D11Device* dx11device, ID3D11Buffer** out_pBuffer, UINT size )
{
// 16の倍数にする
size += 15;
size &= ( ~15 );
D3D11_BUFFER_DESC desc = { 0 };
desc.ByteWidth = size;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
desc.StructureByteStride = 0;
return dx11device->CreateBuffer( &desc, NULL, out_pBuffer );
}
>/code>
しばらく勉強用サイトにならってpaddingデータを詰めた構造体を定義して使っていたけど、メモリ確保の段階でサイズ補正すればいいらしいと、ほかのソースコード眺めてて発見した。
■テクスチャ読み込み
HRESULT create( ID3D11Device* dx11device, LPCTSTR imageFilePath )
{
HRESULT hResult = S_FALSE;
if ( Utility::checkExtension( imageFilePath, TEXT( ".dds" ) ) )
{
hResult = DirectX::CreateDDSTextureFromFile( dx11device, imageFilePath, &m_pTexture, &m_pTextureView );
}
else
{
hResult = DirectX::CreateWICTextureFromFile( dx11device, imageFilePath, &m_pTexture, &m_pTextureView );
}
return hResult;
}
ID3D11Resource* m_pTexture;
ID3D11ShaderResourceView* m_pTextureView;
DirectXTKの"DDSTextureLoader.h"と"WICTextureLoader.h"でテクスチャの読み込みが簡単にできる例。■レンダーターゲットテクスチャ作成
static HRESULT createR16G16B16A16( ID3D11Device* dx11device, UINT width, UINT height,
ID3D11Texture2D** renderTargetTexture,
ID3D11RenderTargetView** renderTargetView,
ID3D11ShaderResourceView** shaderResourceView,
ID3D11UnorderedAccessView** unorderedAccessView = NULL )
{
D3D11_TEXTURE2D_DESC texDesc = { 0 };
texDesc.Width = width;
texDesc.Height = height;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
texDesc.SampleDesc.Count = 1;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE | (unorderedAccessView ? D3D11_BIND_UNORDERED_ACCESS : 0);
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0;
HRESULT hResult = dx11device->CreateTexture2D( &texDesc, NULL, renderTargetTexture );
if ( FAILED( hResult ) ) return hResult;
D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
ZeroMemory( &renderTargetViewDesc, sizeof( renderTargetViewDesc ) );
renderTargetViewDesc.Format = texDesc.Format;
renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
renderTargetViewDesc.Texture2D.MipSlice = 0;
hResult = dx11device->CreateRenderTargetView( *renderTargetTexture, &renderTargetViewDesc, renderTargetView );
if ( FAILED( hResult ) ) return hResult;
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
ZeroMemory( &shaderResourceViewDesc, sizeof( shaderResourceViewDesc ) );
shaderResourceViewDesc.Format = texDesc.Format;
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
shaderResourceViewDesc.Texture2D.MipLevels = 1;
hResult = dx11device->CreateShaderResourceView( *renderTargetTexture, &shaderResourceViewDesc, shaderResourceView );
if ( FAILED( hResult ) ) return hResult;
if ( unorderedAccessView )
{
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
ZeroMemory( &uavDesc, sizeof( uavDesc ) );
uavDesc.Format = DXGI_FORMAT_UNKNOWN;
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
hResult = dx11device->CreateUnorderedAccessView( *renderTargetTexture, &uavDesc, unorderedAccessView );
if ( FAILED( hResult ) ) return false;
}
return hResult;
}
ID3D11RenderTargetViewは描画先に指定するときに使用。ID3D11ShaderResourceViewはテクスチャとして使用するときに使用。
ID3D11UnorderedAccessViewはComputeShaderで使用する。
ここらへんのソースコードはDirectXSDKのほぼそのまま。
■深度テクスチャ作成
static HRESULT createR32( ID3D11Device* dx11device, UINT width, UINT height,
ID3D11Texture2D** depthTexture,
ID3D11DepthStencilView** depthTextureView,
ID3D11ShaderResourceView** depthTextureSRV )
{
D3D11_TEXTURE2D_DESC txDesc = { 0 };
txDesc.Width = width;
txDesc.Height = height;
txDesc.MipLevels = 1;
txDesc.ArraySize = 1;
txDesc.Format = DXGI_FORMAT_R32_TYPELESS;
txDesc.SampleDesc.Count = 1;
txDesc.SampleDesc.Quality = 0;
txDesc.Usage = D3D11_USAGE_DEFAULT;
txDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
txDesc.CPUAccessFlags = 0;
txDesc.MiscFlags = 0;
HRESULT hResult = dx11device->CreateTexture2D( &txDesc, NULL, depthTexture );
if ( FAILED( hResult ) ) return hResult;
D3D11_DEPTH_STENCIL_VIEW_DESC dsDesc = { txDesc.Format };
dsDesc.Format = DXGI_FORMAT_D32_FLOAT;
dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsDesc.Texture2D.MipSlice = 0;
hResult = dx11device->CreateDepthStencilView( *depthTexture, &dsDesc, depthTextureView );
if ( FAILED( hResult ) ) return hResult;
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
ZeroMemory( &shaderResourceViewDesc, sizeof( shaderResourceViewDesc ) );
shaderResourceViewDesc.Format = DXGI_FORMAT_R32_FLOAT;
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
shaderResourceViewDesc.Texture2D.MipLevels = 1;
hResult = dx11device->CreateShaderResourceView( *depthTexture, &shaderResourceViewDesc, depthTextureSRV );
return hResult;
}
0 件のコメント:
コメントを投稿