Система частиц HLSL не будет отображаться

Я пытался добавить систему частиц в мою графическую демонстрацию Directx11, и поэтому я использую книгу "Введение в программирование трехмерных игр с DirectX 11".

Из-за этого я пытаюсь использовать технику HLSL StreamOut для обновления системы частиц и отдельную технику для визуализации частиц.

Ниже приведен код HLSL для системы частиц, я попытался отрегулировать ускорение и скорость в выключенном состоянии, когда скорость посылала частицы за пределы экрана, однако это не имело никакого эффекта.

cbuffer cbPerFrame
{
 float3 gEyePosW;
 
 float3 gEmitPosW;
 float3 gEmitDirW;

 float gGameTime;
 float gTimeStep;

 float4x4 gViewProj;
};

cbuffer cbFixed
{
 // Net constant acceleration used to accerlate the particles.
 float3 gAccelW = {0.0f, 0.2f, 0.0f};

 // Texture coordinates for billbording are always the same - we use a qquad in this effect :)
 float2 gTexC[4] = 
 {
  float2(0.0f, 1.0f),
  float2(0.0f, 0.0f),
  float2(1.0f, 1.0f),
  float2(1.0f, 0.0f)
 };
};

// Nonnumeric values cannot be added to a cbuffer.
Texture2DArray gTextureMapArray;
// Random texture used to generate random numbers in shaders.
Texture1D gRandomTexture;

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

DepthStencilState DisableDepth
{
    DepthEnable = FALSE;
    DepthWriteMask = ZERO;
};
DepthStencilState NoDepthWrites
{
    DepthEnable = TRUE;
    DepthWriteMask = ZERO;
};

BlendState AdditiveBlending
{
    AlphaToCoverageEnable = FALSE;
    BlendEnable[0] = TRUE;
    SrcBlend = SRC_ALPHA;
    DestBlend = ONE;
    BlendOp = ADD;
    SrcBlendAlpha = ZERO;
    DestBlendAlpha = ZERO;
    BlendOpAlpha = ADD;
    RenderTargetWriteMask[0] = 0x0F;
};

///////////////////////////////////////////////////////////////
//  Helper functions
//
///////////////////////////////////////////////////////////////
float3 RandUnitVec3(float offset)
{
 // Use game time plus offset to sample random texture.
 float u = (gGameTime + offset);
 
 // coordinates in [-1,1]
 float3 v = gRandomTexture.SampleLevel(samLinear, u, 0).xyz;
 
 // project onto unit sphere (Normalize)
 return normalize(v);
}

///////////////////////////////////////////////////////////////
//  Stream Out Technique
//
///////////////////////////////////////////////////////////////

#define PT_EMITTER 0
#define PT_FLARE 1

struct Particle
{
 float3 InitPosW : POSITION;
 float3 InitVelW : VELOCITY;
 float2 SizeW : SIZE;
 float Age  : AGE;
 uint Type  : Type;
};

Particle StreamOutVS(Particle vin)
{
 return vin;
}

// The stream-out GS is just responsible for emitting 
// new particles and destroying old particles.  The logic
// programed here will generally vary from particle system
// to particle system, as the destroy/spawn rules will be 
// different.
[maxvertexcount(2)]
void StreamOutGS(point Particle gin[1],
    inout PointStream<Particle> ptStream)
{
 gin[0].Age += gTimeStep;

 // if particle is emitter particle
 if (gin[0].Type == PT_EMITTER)
 {
  // If it's time to emit new particle
  if (gin[0].Age > 0.005f)
  {
   float3 vRandom = RandUnitVec3(0.0f);
   vRandom.x *= 0.5f;
   vRandom.z *= 0.5f;

   Particle p;
   p.InitPosW = gEmitPosW.xyz;
   p.InitVelW = 0.5f*vRandom;
   p.SizeW  = float2(3.0f, 3.0f);
   p.Age  = 0.0f;
   p.Type  = PT_FLARE;

   ptStream.Append(p);

   // reset the time to emit
   gin[0].Age = 0.0f;
  }

  // always keep emitters
  ptStream.Append(gin[0]);
 }
 else
 {
  // Set conditions to keep a particle - in this case age limit
  if (gin[0].Age <= 1.0f)
  {
   ptStream.Append(gin[0]);
  }
 }
}

GeometryShader gsStreamOut = ConstructGSWithSO(
  CompileShader( gs_5_0, StreamOutGS() ),
  "POSITION.xyz; VELOCITY.xyz; SIZE.xyz; AGE.x; TYPE.x" );

technique11 StreamOutTech
{
 pass P0
 {
  SetVertexShader( CompileShader( vs_5_0, StreamOutVS() ) );
  SetGeometryShader( gsStreamOut );

  // disable pixel shader for stream-out only
        SetPixelShader(NULL);
        
        // we must also disable the depth buffer for stream-out only
        SetDepthStencilState( DisableDepth, 0 );
 }
}

///////////////////////////////////////////////////////////////
//  Draw Technique
//
///////////////////////////////////////////////////////////////


struct VertexIn
{
 float3 Pos   : POSITION;
 float2 SizeW     : SIZE;
};

struct VertexOut
{
 float3 PosW   : POSITION;
    float2 SizeW  : SIZE;
 float4 Colour  : COLOR;
 uint Type   : TYPE;
};

VertexOut DrawVS(Particle vin)
{
 VertexOut vout;

 float t = vin.Age;

 // constant Acceleration equation
 vout.PosW = 0.5f*t*t*gAccelW + t*vin.InitVelW + vin.InitPosW;

 // fade colour with time
 float opacity = 1.0f - smoothstep(0.0f, 1.0f, t/1.0f);
 vout.Colour = float4(1.0f, 1.0f, 1.0f, opacity);

 vout.SizeW = vin.SizeW;
 vout.Type = vin.Type;

 return vout;
}

struct GeoOut
{
 float4 PosH  : SV_POSITION;
    float4 Colour : COLOR;
    float2 Tex  : TEXCOORD;
};

// Expand each 'Point' into a quad (4 verticies)
[maxvertexcount(4)]
void DrawGS(point VertexOut gin[1],
  inout TriangleStream<GeoOut> triStream)
{
 // Do not draw Emiter particles in this system
 if (gin[0].Type != PT_EMITTER)
 {
  //
  // Compute world matrix so that billboard faces the camera.
  //
  float3 look  = normalize(gEyePosW.xyz - gin[0].PosW);
  float3 right = normalize(cross(float3(0,1,0), look));
  float3 up    = cross(look, right);

  //
  // Compute triangle strip vertices (quad) in world space.
  //
  float halfWidth  = 0.5f*gin[0].SizeW.x;
  float halfHeight = 0.5f*gin[0].SizeW.y;

  float4 v[4];
  v[0] = float4(gin[0].PosW + halfWidth*right - halfHeight*up, 1.0f);
  v[1] = float4(gin[0].PosW + halfWidth*right + halfHeight*up, 1.0f);
  v[2] = float4(gin[0].PosW - halfWidth*right - halfHeight*up, 1.0f);
  v[3] = float4(gin[0].PosW - halfWidth*right + halfHeight*up, 1.0f);

  //
  // Transform quad vertices to world space and output 
  // them as a triangle strip.
  //
  GeoOut gout;
  [unroll]
  for(int i = 0; i < 4; ++i)
  {
   gout.PosH  = mul(v[i], gViewProj);
   gout.Tex   = gTexC[i];
   gout.Colour = gin[0].Colour;
   triStream.Append(gout);
  }
 } 
}

float DrawPS(GeoOut pin) : SV_TARGET
{
 return gTextureMapArray.Sample(samLinear, float3(pin.Tex, 0)) * pin.Colour;
}

technique11 DrawTech
{
    pass P0
    {
        SetVertexShader(   CompileShader( vs_5_0, DrawVS() ) );
        SetGeometryShader( CompileShader( gs_5_0, DrawGS() ) );
        SetPixelShader(    CompileShader( ps_5_0, DrawPS() ) );
        
        SetBlendState(AdditiveBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xffffffff);
        SetDepthStencilState( NoDepthWrites, 0 );
    }
}

Ниже приведен код для построения VB.

void ParticleSystem::BuildVB(ID3D11Device* device)
{
 /////////////////////////////////////////////////////////
 // Create the buffer to start the particle system.
 /////////////////////////////////////////////////////////

 D3D11_BUFFER_DESC vbd;
    vbd.Usage = D3D11_USAGE_DEFAULT;
 vbd.ByteWidth = sizeof(Vertex::Particle) * 1;
    vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vbd.CPUAccessFlags = 0;
    vbd.MiscFlags = 0;
 vbd.StructureByteStride = 0;

 // The initial particle emitter has type 0 and age 0.  The rest
 // of the particle attributes do not apply to an emitter.
 Vertex::Particle p;
 ZeroMemory(&p, sizeof(Vertex::Particle));
 p.Age  = 0.0f;
 p.Type = 0; 

 D3D11_SUBRESOURCE_DATA vinitData;
    vinitData.pSysMem = &p;

 HR(device->CreateBuffer(&vbd, &vinitData, &mInitVB));

 //////////////////////////////////////////////////////////////
 // Create the  buffers which swap back and forth for stream-out and drawing.
 //////////////////////////////////////////////////////////////

 vbd.ByteWidth = sizeof(Vertex::Particle) * mMaxParticles;
    vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_STREAM_OUTPUT;

 HR(device->CreateBuffer(&vbd, 0, &mDrawVB));
 HR(device->CreateBuffer(&vbd, 0, &mStreamOutVB));
}

А теперь код Draw cpp.

void ParticleSystem::Draw(ID3D11DeviceContext* dc, const XMMATRIX& viewProj)
{
 //
 // Set constants.
 //
 mFX->SetViewProj(viewProj);
 mFX->SetGameTime(mGameTime);
 mFX->SetTimeStep(mTimeStep);
 mFX->SetEyePosW(mEyePosW);
 mFX->SetEmitPosW(mEmitPosW);
 mFX->SetEmitDirW(mEmitDirW);
 mFX->SetTexArray(mTextureArraySRV);
 mFX->SetRandomTex(mRandomTextureSRV);

 //
 // Set IA stage.
 //
 dc->IASetInputLayout(InputLayouts::Particle);
    dc->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);

 UINT stride = sizeof(Vertex::Particle);
    UINT offset = 0;

 // On the first pass, use the initialization VB.  Otherwise, use
 // the VB that contains the current particle list.
 if( mFirstRun )
  dc->IASetVertexBuffers(0, 1, &mInitVB, &stride, &offset);
 else
  dc->IASetVertexBuffers(0, 1, &mDrawVB, &stride, &offset);

 //
 // Draw the current particle list using stream-out only to update them.  
 // The updated vertices are streamed-out to the target VB. 
 //
 dc->SOSetTargets(1, &mStreamOutVB, &offset);

  D3DX11_TECHNIQUE_DESC techDesc;
 mFX->StreamOutTech->GetDesc( &techDesc );
    for(UINT p = 0; p < techDesc.Passes; ++p)
    {
        mFX->StreamOutTech->GetPassByIndex( p )->Apply(0, dc);
        
  if(mFirstRun)
  {
   dc->Draw(1, 0);
   mFirstRun = false;
  }
  else
  {
   dc->DrawAuto();
  }
    }
 // done streaming-out--unbind the vertex buffer
 ID3D11Buffer* bufferArray[1] = {0};
 dc->SOSetTargets(1, bufferArray, &offset);

 // ping-pong the vertex buffers
 std::swap(mDrawVB, mStreamOutVB);

 //
 // Draw the updated particle system we just streamed-out. 
 //
 dc->IASetVertexBuffers(0, 1, &mDrawVB, &stride, &offset);

 mFX->DrawTech->GetDesc( &techDesc );
    for(UINT p = 0; p < techDesc.Passes; ++p)
    {
        mFX->DrawTech->GetPassByIndex( p )->Apply(0, dc);
        
  dc->DrawAuto();
    }

}

Я думал, что, возможно, причиной этого может быть либо состояние смешивания, либо состояние глубины, используемое некоторыми другими объектами в моей сцене (возможно, я неправильно понял то, что установил ранее). Я попытался удалить весь другой код рендеринга, оставив только код отрисовки выше, но безрезультатно.

На мой взгляд, я могу думать только о нескольких возможных причинах этой проблемы, но пока не могу найти решение.

  1. Масштаб системы не подходит для моей сцены. например. частицы притягиваются, но быстро уходят с экрана, чтобы их можно было увидеть.
    • Как упомянуто выше, я попытался удалить ускорение и скорость частиц в коде HLSL, чтобы увидеть неподвижные частицы. Это не имело никакого эффекта.
  2. Неверное состояние смешивания / состояние шаблона глубины. Например. Как уже упоминалось выше.
  3. Частица Эмиттера, по некоторым причинам, не производится / размещается правильно. не вызывая образования "вытягиваемых" частиц по очереди. Поскольку большая часть этого кода находится в файле.fx, я не могу пройтись, чтобы проверить частицы эмиттера. Я думаю, что это более вероятная проблема, но я ошибался раньше.

Любая помощь по этому вопросу будет принята с благодарностью, я хорошо и действительно застрял на этом. Ниже я добавил дополнительные фрагменты кода, которые могут быть полезны.

EG Входная раскладка.

const D3D11_INPUT_ELEMENT_DESC InputLayoutDesc::Particle[5] = 
{
 {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0},
 {"VELOCITY", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
 {"SIZE",     0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0},
 {"AGE",      0, DXGI_FORMAT_R32_FLOAT,       0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0},
 {"TYPE",     0, DXGI_FORMAT_R32_UINT,        0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0},
};

И система частиц ИНИТ

mFire.Init(md3dDevice, AppEffects::FireFX, mFlareTextureSRV, mRandomTextureSRV, 500);
 mFire.SetEmitPos(XMFLOAT3(1.0f, 0.5f, 0.0f));


void ParticleSystem::Init(ID3D11Device* device, ParticleEffect* fx, 
  ID3D11ShaderResourceView* textureArraySRV, 
  ID3D11ShaderResourceView* randomTextureSRV, 
  UINT maxParticles)
{
 mMaxParticles = maxParticles;

 mFX = fx;

 mTextureArraySRV = textureArraySRV;
 mRandomTextureSRV = randomTextureSRV;

 BuildVB(device);
}

Если я пропустил какой-либо раздел кода, который вам нужно увидеть, дайте мне знать.

Заранее спасибо.

1 ответ

Решение

Извините за этот глупый вопрос всем. Оказывается, это была глупая ошибка в пиксельном шейдере. Я установил пиксельный шейдер как float вместо float4.

Другие вопросы по тегам