【问题标题】:HLSL Particle system will not displayHLSL 粒子系统不会显示
【发布时间】:2015-04-08 01:57:22
【问题描述】:

我一直在尝试将粒子系统添加到我的 Directx11 图形演示中,因此我一直在使用“Directx 11 3d 游戏编程简介”一书。

因此,我尝试使用 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 文件中,我无法逐步检查发射器粒子。我认为这是更可能的问题,但我以前错了。

对此的任何帮助将不胜感激,我完全坚持这一点。 下面我添加了额外的可能有用的代码 sn-ps。

例如输入布局。

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);
}

如果我遗漏了您需要查看的任何代码部分,请告诉我。

提前致谢。

【问题讨论】:

    标签: c++ directx directx-11 hlsl particle-system


    【解决方案1】:

    对不起这个愚蠢的问题。事实证明这是像素着色器中的一个愚蠢错误。我将像素着色器设置为浮点数而不是浮点数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多