Advertisement Jump to content
Sign in to follow this  
Jacob Mnasin

DX11 Particle System/Geometry Shader Help

This topic is 1882 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello all, 

 

Im trying to implement a particle system from Frank Lunas Intro to DX11 book. I'm trying to do so without using the effect framework and have been successful for many of the topics, but I am completely stumped on this one. Mostly because my understanding of the geometry shader is lacking. I've read through the guide on Microsoft's site but it is hard to follow and had some incorrect code.

 

particle declaration:

struct Particle
{
	XMFLOAT3 InitialPos;
	XMFLOAT3 InitialVel;
	XMFLOAT2 Size;
	float Age;
	unsigned int Type;
};


struct ParticleBuffer
{
	XMFLOAT3 gEyePosW;

	// for when the emit position/direction is varying
	XMFLOAT3 gEmitPosW;
	XMFLOAT3 gEmitDirW;

	float gGameTime;
	float gTimeStep;
	XMFLOAT4X4 gViewProj; 
	float pad;
};

vertex declaration:

D3D11_INPUT_ELEMENT_DESC particleLayout[5] = 
{
	{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0},
	{"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
	{"TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0},
	{"TEXCOORD", 2, DXGI_FORMAT_R32_FLOAT,       0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0},
	{"TEXCOORD", 3, DXGI_FORMAT_R32_FLOAT,        0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0},
};

ParticleSystem.cpp


#include "ParticleSystem.h"
#include "Vertex.h"
#include "BaseCamera.h"
 
ParticleSystem::ParticleSystem()
: mInitVB(0), mDrawVB(0), mStreamOutVB(0), mTexArraySRV(0), mRandomTexSRV(0)
{
	mFirstRun = true;
	mGameTime = 0.0f;
	mTimeStep = 0.0f;
	mAge      = 0.0f;

	mEyePosW  = XMFLOAT3(0.0f, 0.0f, 0.0f);
	mEmitPosW = XMFLOAT3(0.0f, 0.0f, 0.0f);
	mEmitDirW = XMFLOAT3(0.0f, 1.0f, 0.0f);
}

ParticleSystem::~ParticleSystem()
{
	ReleaseCOM(mInitVB);
	ReleaseCOM(mDrawVB);
	ReleaseCOM(mStreamOutVB);
}

float ParticleSystem::GetAge()const
{
	return mAge;
}

void ParticleSystem::SetEyePos(const XMFLOAT3& eyePosW)
{
	mEyePosW = eyePosW;
}

void ParticleSystem::SetEmitPos(const XMFLOAT3& emitPosW)
{
	mEmitPosW = emitPosW;
}

void ParticleSystem::SetEmitDir(const XMFLOAT3& emitDirW)
{
	mEmitDirW = emitDirW;
}

void ParticleSystem::Init(ID3D11Device* device, ID3D11ShaderResourceView* texArraySRV, 
	                      ID3D11ShaderResourceView* randomTexSRV, UINT maxParticles)
{
	mMaxParticles = maxParticles;


	mTexArraySRV  = texArraySRV;
	mRandomTexSRV = randomTexSRV; 

	BuildVB(device);
	BuildMB(device);
	CreateShader(device);
}

void ParticleSystem::Reset()
{
	mFirstRun = true;
	mAge      = 0.0f;
}

void ParticleSystem::Update(float dt, float gameTime)
{
	mGameTime = gameTime;
	mTimeStep = dt;

	mAge += dt;
}

void ParticleSystem::Draw(ID3D11DeviceContext* dc, BaseCamera* cam, XMFLOAT4X4 projectionMatrix)
{
	XMMATRIX VP = (XMMatrixMultiply(cam->GetViewMatrix(), XMLoadFloat4x4(&projectionMatrix)));
	ParticleBuffer* dataPtr;
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	//
	// Set constants.
	//
	dc->Map(mBuff, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	dataPtr = (ParticleBuffer*)mappedResource.pData;	
	XMStoreFloat4x4(&dataPtr->viewProj, VP);
	dataPtr->mGameTime = mGameTime;
	dataPtr->mTimeStep = mTimeStep;
	dataPtr->mEyePosW = mEyePosW;
	dataPtr->mEmitPosW = mEmitPosW;
	dataPtr->mEmitDirW = mEmitDirW;
	dataPtr->gEyePosW = XMFLOAT4(cam->Transform->GetPosition().x, cam->Transform->GetPosition().y, cam->Transform->GetPosition().z, 1);
	dc->Unmap(mBuff, 0);
	dc->PSSetConstantBuffers(0, 1, &mBuff);

	dc->PSSetShaderResources(0, 1, &mTexArraySRV);
	dc->PSSetShaderResources(1, 1, &mRandomTexSRV);

	//
	// Set IA stage.
	//
	dc->IASetInputLayout(pVertexLayout);
    dc->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);

	UINT stride = sizeof(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);

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

	dc->VSSetShader(vertexShader, NULL, 0);
	//dc->GSSetShader(geoShader, NULL, 0);
	dc->PSSetShader(pixelShader, NULL, 0);

	// Set the sampler state in the pixel shader.
	dc->PSSetSamplers(0, 1, &sampleState);

	dc->DrawAuto();
    
}

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

    D3D11_BUFFER_DESC vbd;
    vbd.Usage = D3D11_USAGE_DEFAULT;
	vbd.ByteWidth = sizeof(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.
	Particle p;
	ZeroMemory(&p, sizeof(Particle));
	p.Age  = 0.0f;
	p.Type = 0; 
 
    D3D11_SUBRESOURCE_DATA vinitData;
    vinitData.pSysMem = &p;

	device->CreateBuffer(&vbd, &vinitData, &mInitVB);
	
	//
	// Create the ping-pong buffers for stream-out and drawing.
	//
	vbd.ByteWidth = sizeof(Particle) * mMaxParticles;
    vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_STREAM_OUTPUT;

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



void ParticleSystem::BuildMB(ID3D11Device* device)
{
	D3D11_BUFFER_DESC matrixBufferDesc;
	// Setup the description of the dynamic matrix constant buffer that is in the vertex shader.
	matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	matrixBufferDesc.ByteWidth = sizeof(ParticleBuffer);
	matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	matrixBufferDesc.MiscFlags = 0;
	matrixBufferDesc.StructureByteStride = 0;

	// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
	device->CreateBuffer(&matrixBufferDesc, NULL, &mBuff);
}

bool ParticleSystem::CreateShader(ID3D11Device* device)
{
	ID3DBlob* pixelShaderBuffer;
	ID3DBlob* geoShaderBuffer;
	ID3DBlob* vertexShaderBuffer;
	D3D11_SAMPLER_DESC samplerDesc;
	bool result;


	LPCSTR materialShaderName = "Assets/EngineParticle.fx";
	string shaderVersion = "4_0";

	string vs = "vs_" + shaderVersion;
	result = D3DX11CompileFromFileA(materialShaderName, NULL, NULL, "VS", vs.c_str(), D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, 
		&vertexShaderBuffer, NULL, NULL);
	if(FAILED(result))
	{
		return false;
	}


	vs = "gs_4_0";
	result = D3DX11CompileFromFileA(materialShaderName, NULL, NULL, "GS", vs.c_str(), D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, 
		&geoShaderBuffer, NULL, NULL);
	if(FAILED(result))
	{
		return false;
	}


	vs = "ps_" + shaderVersion;
	result = D3DX11CompileFromFileA(materialShaderName, NULL, NULL, "PS", vs.c_str(), D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, 
		&pixelShaderBuffer, NULL, NULL);
	if(FAILED(result))
	{
		return false;
	}



	result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &vertexShader);
	if(FAILED(result))
	{
		return false;
	}

	D3D11_SO_DECLARATION_ENTRY pDecl[3] =
	{
		// semantic name, semantic index, start component, component count, output slot
		{ 0, "SV_POSITION", 0, 0, 4, 0 },   // output all components of position
		{ 0, "COLOR", 0, 0, 4, 0 },     // output the first 3 of the normal
		{ 0, "TEXCOORD0", 0, 0, 2, 0 },		// output the first 2 texture coordinates
	};

	result = device->CreateGeometryShaderWithStreamOutput(geoShaderBuffer->GetBufferPointer(), geoShaderBuffer->GetBufferSize(), pDecl, sizeof(pDecl), NULL, 0, 0, NULL, &geoShader);
	//result = device->CreateGeometryShader(geoShaderBuffer->GetBufferPointer(), geoShaderBuffer->GetBufferSize(), NULL, &geoShader);
	if(FAILED(result))
	{
		return false;
	}


	result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &pixelShader);
	if(FAILED(result))
	{
		return false;
	}

	// Create a texture sampler state description.
	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.MipLODBias = 0.0f;
	samplerDesc.MaxAnisotropy = 1;
	samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	samplerDesc.BorderColor[0] = 0;
	samplerDesc.BorderColor[1] = 0;
	samplerDesc.BorderColor[2] = 0;
	samplerDesc.BorderColor[3] = 0;
	samplerDesc.MinLOD = 0;
	samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

	// Create the texture sampler state.
	result = device->CreateSamplerState(&samplerDesc, &sampleState);
	if(FAILED(result))
	{
		return false;
	}


	pixelShaderBuffer->Release();

	InitializeLayout(device, vertexShaderBuffer);
	vertexShaderBuffer->Release();
}

void ParticleSystem::InitializeLayout( ID3D11Device* device, ID3DBlob* vertexShaderBuffer)
{
	HRESULT hr;
	if(!vertexShaderBuffer)
	{
		return;
	}
	// Get a count of the elements in the layout.
	int numElements = sizeof(particleLayout) / sizeof(particleLayout[0]);
	hr = device->CreateInputLayout(particleLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &pVertexLayout);

}

Game.cpp

// Initialize
mFlareTexSRV = content->LoadTexture(L"assets/flare0.dds");
particle.Init(device,mFlareTexSRV, mFlareTexSRV, 500); 
particle.SetEmitPos(XMFLOAT3(0.0f, 1.0f, 0.0f));

//Update
particle.Update(gameTime, gameTimeElapsed);

//Render
float blendFactor[] = {0.0f, 0.0f, 0.0f, 0.0f};
particle.SetEyePos(m_Camera->Transform->GetPosition());
particle.Draw(context, m_Camera, projectionMatrix);
context->OMSetBlendState(0, blendFactor, 0xffffffff);

Particle.Fx

//***********************************************
// GLOBALS                                      *
//***********************************************

cbuffer MatrixBuffer: register(b0)
{
	float3 gEyePosW;
	
	// for when the emit position/direction is varying
	float3 gEmitPosW;
	float3 gEmitDirW;
	
	float gGameTime;
	float gTimeStep;
	float4x4 gViewProj; 
};

cbuffer cbFixed
{
	// Net constant acceleration used to accerlate the particles.
	float3 gAccelW = {0.0f, 7.8f, 0.0f};
	
	// Texture coordinates used to stretch texture over quad 
	// when we expand point particle into a quad.
	float2 gQuadTexC[4] = 
	{
		float2(0.0f, 1.0f),
		float2(1.0f, 1.0f),
		float2(0.0f, 0.0f),
		float2(1.0f, 0.0f)
	};
};
 
// Array of textures for texturing the particles.
Texture2DArray gTexArray;

// Random texture used to generate random numbers in shaders.
Texture1D gRandomTex;
 
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 = gRandomTex.SampleLevel(samLinear, u, 0).xyz;
	
	// project onto unit sphere
	return normalize(v);
}
 
//***********************************************
// STREAM-OUT TECH                              *
//***********************************************

#define PT_EMITTER 0
#define PT_FLARE 1
 
struct Particle
{
	float3 InitialPosW : POSITION;
	float3 InitialVelW : TEXCOORD0;
	float2 SizeW       : TEXCOORD1;
	float Age          : TEXCOORD2;
	uint Type          : TEXCOORD3;
};
  
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( gin[0].Type == PT_EMITTER )
	{	
		// time to emit a new particle?
		if( gin[0].Age > 0.005f )
		{
			float3 vRandom = RandUnitVec3(0.0f);
			vRandom.x *= 0.5f;
			vRandom.z *= 0.5f;
			
			Particle p;
			p.InitialPosW = gEmitPosW.xyz;
			p.InitialVelW = 4.0f*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
	{
		// Specify conditions to keep particle; this may vary from system to system.
		if( gin[0].Age <= 1.0f )
			ptStream.Append(gin[0]);
	}		
}

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

//***********************************************
// DRAW TECH                                    *
//***********************************************

struct VertexOut
{
	float3 PosW  : POSITION;
	float2 SizeW : TEXCOORD1;
	float4 Color : COLOR;
	uint   Type  : TEXCOORD3;
};

VertexOut VS(Particle vin)
{
	VertexOut vout;
	
	float t = vin.Age;
	
	// constant acceleration equation
	vout.PosW = 0.5f*t*t*gAccelW + t*vin.InitialVelW + vin.InitialPosW;
	
	// fade color with time
	float opacity = 1.0f - smoothstep(0.0f, 1.0f, t/1.0f);
	vout.Color = 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 Color : COLOR;
	float2 Tex   : TEXCOORD;
};

// The draw GS just expands points into camera facing quads.
[maxvertexcount(4)]
void GS(point VertexOut gin[1], 
            inout TriangleStream<GeoOut> triStream)
{	
	// do not draw emitter particles.
	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   = gQuadTexC[i];
			gout.Color = gin[0].Color;
			triStream.Append(gout);
		}	
	}
}

float4 PS(GeoOut pin) : SV_TARGET
{
	return gTexArray.Sample(samLinear, float3(pin.Tex, 0))*pin.Color;
}

I do not get any kind of errors and the code seems to compile just fine. However, the particles do not render.

Sorry for the large amount of code...I didnt even know where to look to solve this problem.

Edited by JacobM

Share this post


Link to post
Share on other sites
Advertisement

Haven't gone through the all code, but the first issue I see is with ParticleBuffer - it has packing rules issues (yes, read the entire article).

Buffer elements can't cross 4-floats boundary. For example, if you define in the fx file

float3 a;
float3 b;

The HSLS compiler will automatically add padding, and the struct will look like:

float3 a;
float pad0;
float3 b;
float pad1;

This is not true for C++ and C#. When you define 2 float3 in C#, the compiler leave them like that, you have to pad the structs by yourself.
 

You can rearrange the struct a bit:

struct ParticleBuffer
{
	XMFLOAT4X4 gViewProj;   // Aligned on 4 floats

	XMFLOAT3 gEyePosW;   // 3 floats
	float gGameTime;            // 1 float, fits into the 4 floats boundary

	XMFLOAT3 gEmitPosW; // 3 floats
	float gTimeStep;             // and another 1 that fits into the same float4 vector

	XMFLOAT3 gEmitDirW;  // same as the other 2
	float pad;
};

You'll need to change the buffer definition in the HLSL accordingly. Again, haven't read your whole code, but this is clearly wrong, so start with that.

Share this post


Link to post
Share on other sites

NIB: Thanks for the suggestion, I knew about the 4 float packing rule but I never realized that the order made a difference. However I fixed that part of the code but still nothing shows. I suspect it has something to do with either how I am creating/calling the geometry shader, the draw call or the shader itself.

Share this post


Link to post
Share on other sites

I was able to narrow down the problem to a single line of code (so far):

 

device->CreateGeometryShaderWithStreamOutput(geoShaderBuffer->GetBufferPointer(), geoShaderBuffer->GetBufferSize(), pDecl, sizeof(pDecl), NULL, 0, 0, NULL, &geoShader);

 

It seems after this line, the geometry shader gets set to 0x00000000 but doesnt actually report that it failed to create. This is my first attempt with working with geometry shader so I have no idea what I'm doing wrong. Can anyone please help?

Edited by JacobM

Share this post


Link to post
Share on other sites
Funny you get no failure at all. You haven't defined the output stride(s). These are mandatory parameters: pBufferStrides and NumStrides. It should be sizeof(Particle), but currently you're streaming out the wrong geometry shader (see further below).

With strides defined I do get some feedback:

D3D11: ERROR: ID3D11Device::CreateGeometryShaderWithStreamOutput: Stream Output Declaration Element[2]: SemanticName string ("TEXCOORD0") cannot end with a number...

Classic, everyone stumbles across this one at least once. This should work:
D3D11_SO_DECLARATION_ENTRY pDecl[3] =
{
    { 0, "SV_POSITION", 0, 0, 4, 0 },   
    { 0, "COLOR", 0, 0, 4, 0 },     
    { 0, "TEXCOORD", 0, 0, 2, 0 }, // Note: NO "0" in the semantic string
};
Now I wonder why you wanna stream out the point expansion (the "drawing" geometry shader) instead of the physics update (StreamOutGS) wink.png.

I would actually recommend PIX, if it wasn't so reluctant with stream out. Maybe the VS 2012 graphics debugger is better in this regard. Alternatively help yourself with a staging buffer to read back and check if you got the stride and stream out layout right. Work with a simple pass through GS first.

As an aside: You can abandon the D3D9 semantics naming (TEXCOORD#). From D3D10 onwards you can name them arbitrarily, just like Luna did.

Share this post


Link to post
Share on other sites

Hey thanks for the reply! I will def try your suggestions. The reason I left D3D9 semantics is because I am trying to preserve compatibility with Directx 9 (although I do understand that the geometry shader will not be available. This is why I declared the weird string before the shader compiler 

string shaderVersion = "4_0";

string vs = "vs_" + shaderVersion;

Where shader version will change based on the feature level.

 

 

 

Now I wonder why you wanna stream out the point expansion (the "drawing" geometry shader) instead of the physics update (StreamOutGS) wink.png.

I'm not sure....I've tried to keep the code identical to the original, changing only the parts that were necessary to get it working without the effects framework. The shader pretty much stayed almost identical to what it was in the book. From my understanding, its supposed to send a list of points to the shader, which then streams it out to the geometry shader and turns them into quads. 

 

Would you mind elaborating a bit more on what you mean?

 

I'll give PIX a whirl, ive worked with the VS2012 graphic debugger once before, but I'm not sure if that would work in this case since you have to take a picture of the screen and then click on a pixel you want to examine. This would be tough in my case since nothing was showing.

Share this post


Link to post
Share on other sites

I'm not sure....I've tried to keep the code identical to the original, changing only the parts that were necessary to get it working without the effects framework. The shader pretty much stayed almost identical to what it was in the book. From my understanding, its supposed to send a list of points to the shader, which then streams it out to the geometry shader and turns them into quads. 
 
Would you mind elaborating a bit more on what you mean?

Sure, have a look at the techniques of Luna's Fire.fx effect:

GeometryShader gsStreamOut = ConstructGSWithSO( 
	CompileShader( gs_5_0, StreamOutGS() ), 
	"POSITION.xyz; VELOCITY.xyz; SIZE.xy; 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 );
    }
}
//....
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 );
    }
}
One technique is responsible for stream out (the particle update, particle creation/destruction etc.), the other for drawing (techniques and shaders are named accordingly).

The stream out GS outputs "points", and the there's no pixel shader bound.
The drawing GS on the other hand outputs triangles. Actually one doesn't necessarily need a GS for quad expansion, there are other ways (e.g. instancing or a special VS). Maybe this is the reason for the confusion.

As far as I can tell from your code, you only compile and use the drawing shaders, for both stream out and drawing, which is wrong.

If you wanna get rid of the effect framework this is what you do:
  • Compile and bind the shaders individually. This means all the shaders of one technique (actually of one pass, but there's only one pass per technique).
  • The effect framework lets you define and set states in the fx file (e.g. SetDepthStencilState). With raw shaders you need to create and set them manually (Actually you do this right with the linear sampler).
  • Make sure your matrices have the right majorness. Usually one has to transpose them for sending them to constant buffers. HLSL defaults to column major layout, the effect framework does this automatically.

I'll give PIX a whirl, ive worked with the VS2012 graphic debugger once before, but I'm not sure if that would work in this case since you have to take a picture of the screen and then click on a pixel you want to examine. This would be tough in my case since nothing was showing.

PIX/graphics debugger can do more than just debugging pixels. You can check what buffers, shaders and states are set. In PIX you can even inspect what the buffers contain. This is especially useful for your current setup (checking constant buffers and what the stream out spits out).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!