"Sorting out" render order

Started by
31 comments, last by cozzie 11 years, 2 months ago

Hi Niello.

Thanks again for your feedback on the approach.

I actually complete rewrote my renderqueue when I introduced 'indices'/ scene graph also for each effect and with opaque/blended split up.

All working great now, really happy with it. Profiling with PIX tells me that I eliminated ALL unnecessary setting of materials, states, buffers etc.

Next step; lighting system

Probably combined with introducing spatial culling, to prevent limiting myself to 8 point lights in the 'world'.

Here's the latest version of my demo, if you'd like to take a peek: www.sierracosworth.nl/gamedev/2013-02-10-demo.zip

(controls: W/S/A/D, PG UP/PG DWN)

I've posted just the 3d class rendering code, to give you an impression. Input and feedback always welcome :)

(I didn't post all renderqueue creation code, would be a waste)


/**************************************************************************************/
/***							RENDERFRAME											***/
/*** ==> usage: in main loop, for each frame; RenderQueue added 24-1-2013			***/
/*** ==> render a frame with 3d scene; 1. update cam and scene 2. render fx			***/
/**************************************************************************************/

bool CD3d::RenderFrame(CD3dscene *pD3dscene, CD3dcam *pCam)
{
	if(!CheckDevice()) { mDeviceLost = true; return true; }
	mDrawCallsPerFrame = 0;			mDrawTriPerFrame = 0;

	pCam->Update();

	/** CULLING AND SORTING	**/
	if(!UpdateScene(pD3dscene, pCam)) return false;

	mD3ddev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
	mD3ddev->BeginScene();

	/** SET SHARED FX/ SHADER PARAMETERS **/
	if(D3DERR_INVALIDCALL == pD3dscene->mEffect[0]->SetMatrix("ViewProj", &pCam->mMatViewProjection)) return false;	// SHARED PAREMETER IN POOL
	
	/** RENDER SCENE USING FX/ SHADER WITH SPECIFIC TECHNIQUE **/
	if(!RenderSceneOpaque(pD3dscene, pCam, "OpaqueShader")) return false;
	if(!RenderSceneBlended(pD3dscene, pCam, "BlendingShader")) return false;

	if(pD3dscene->mSkyBoxInScene) if(!pD3dscene->mSkyBox.Render(pCam->mPosition, pCam, mD3ddev)) return false;

	/** FFP RENDERING, I.E. SCENE STATISTICS **/		/** not used: SetDefaultRenderStates() doesn't do anything now **/
	PrintSceneInfo(pCam, pD3dscene->mNrMaterials);		

	/** PRESENT THE FINAL RENDERED SCENE FROM BACKBUFFER **/
	mD3ddev->EndScene();
	HRESULT hr = mD3ddev->Present(NULL, NULL, NULL, NULL); 
	OutputDebugRenderInfo(hr);

	return true;
}

/**************************************************************************************/
/***							UPDATE SCENE										***/
/*** ==> usage: within renderframe, before draw calls etc.							***/
/*** ==> checks all meshes against space positioning, frustum etc.					***/
/**************************************************************************************/

bool CD3d::UpdateScene(CD3dscene *pD3dscene, CD3dcam *pCam)
{
	/** UPDATE DISTANCE TO CAM FOR BLENDED MESH INSTANCES				**/													// TODO
	for(fx=0;fx<mRenderQueue.mNrEffects;++fx)
	{
		for(m=0;m<mRenderQueue.mEffect[fx].nrMeshesBlended;++m)
		{
			for(mi=0;mi<mRenderQueue.mEffect[fx].meshInstBlended[m].nrInstances;++mi)
				pD3dscene->mMeshInstances[mRenderQueue.mEffect[fx].meshInstBlended[m].instances[mi]].UpdateDistToCam(pCam->mPosition);
		}
	}

	/** SORT BLENDED MESH INSTANCES, BACK TO FRONT						**/
	if(!mRenderQueue.SortBlendedMeshes(pD3dscene)) return false;

	/** UPDATE WORLD MATRIX, FOR DYNAMIC MESH INSTANCES ONLY			**/
	for(mi=0;mi<mRenderQueue.mNrMeshInstDynamic;++mi)
		pD3dscene->mMeshInstances[mRenderQueue.mDynamicMeshInstIndex[mi]].UpdateWorldMatrix();

	// TODO here; introduce tree - spatial culling
	
	/** CULL MESH INSTANCES AGAINST FRUSTUM, VISIBLE YES/NO				**/
	for(mi=0;mi<mRenderQueue.mNrMeshInst;++mi)
	{
		if(pCam->SphereInFrustum(&pD3dscene->mMeshInstances[mi].mWorldPos, pD3dscene->mMeshInstances[mi].mBoundingRadius))
			mRenderQueue.mMeshInst[mi].visible = true;
		else mRenderQueue.mMeshInst[mi].visible = false;
	}
	return true;
}

/**************************************************************************************/
/***							RENDER SCENE - OPAQUE								***/
/*** ==> usage: within renderframe, to render specific technique of shader			***/
/*** ==> renders all opaque mesh instances through renderqueue for all effects		***/
/**************************************************************************************/

bool CD3d::RenderSceneOpaque(CD3dscene *pD3dscene, CD3dcam *pCam, char *pTechnique)
{
	for(fx=0;fx<mRenderQueue.mNrEffects;++fx)		
	{
		if(!SetShaderTechnique(pD3dscene, fx, pTechnique)) return false;	
		pD3dscene->mEffect[fx]->Begin(&pD3dscene->mEffectNumPasses[fx], D3DXFX_DONOTSAVESTATE);		

		for(_i=0;_i<pD3dscene->mEffectNumPasses[fx];++_i)
		{
			pD3dscene->mEffect[fx]->BeginPass(_i);
			for(mat=0;mat<mRenderQueue.mEffect[fx].nrMaterialsOpaque;++mat)									
			{
				matid = mRenderQueue.mEffect[fx].materialsOpaque[mat];
				if(!pD3dscene->PreSelectMaterial(matid, fx)) return false;	
				for(m=0;m<mRenderQueue.mEffect[fx].nrMeshesOpaque;++m)											
				{
					mesh = mRenderQueue.mEffect[fx].meshOpaque[m];
					if(!pD3dscene->mMeshes[mesh].SetBuffers(mD3ddev)) return false;					
					for(mi=0;mi<mRenderQueue.mEffect[fx].meshInstOpaque[m].nrInstances;++mi)				
					{
						instance = mRenderQueue.mEffect[fx].meshInstOpaque[m].instances[mi];				
						if(mRenderQueue.mMeshInst[instance].visible)	// (MICRO)optimization? Sort per frame
						{
							if(!pD3dscene->PreSelectMeshInst(instance, mD3ddev)) return false;
							pD3dscene->mEffect[fx]->CommitChanges();

							RenderSubMeshes(pD3dscene, matid, mesh);					
						}
					}
				}
			}
			pD3dscene->mEffect[fx]->EndPass();
		}
		pD3dscene->mEffect[fx]->End();
	}
	return true;
}

/**************************************************************************************/
/***							RENDER SCENE - BLENDED								***/
/*** ==> usage: within renderframe, to render specific technique of shader			***/
/*** ==> renders all blended mesh instances through renderqueue for all effects		***/
/**************************************************************************************/

bool CD3d::RenderSceneBlended(CD3dscene *pD3dscene, CD3dcam *pCam, char *pTechnique)
{
	for(fx=0;fx<mRenderQueue.mNrEffects;++fx)		
	{
		if(!SetShaderTechnique(pD3dscene, fx, pTechnique)) return false;	
		pD3dscene->mEffect[fx]->Begin(&pD3dscene->mEffectNumPasses[fx], D3DXFX_DONOTSAVESTATE);		

		for(_i=0;_i<pD3dscene->mEffectNumPasses[fx];++_i)
		{
			pD3dscene->mEffect[fx]->BeginPass(_i);
			for(mat=0;mat<mRenderQueue.mEffect[fx].nrMaterialsBlended;++mat)									
			{
				matid = mRenderQueue.mEffect[fx].materialsBlended[mat];
				if(!pD3dscene->PreSelectMaterial(matid, fx)) return false;	
				for(m=0;m<mRenderQueue.mEffect[fx].nrMeshesBlended;++m)											
				{
					mesh = mRenderQueue.mEffect[fx].meshBlended[m];
					if(!pD3dscene->mMeshes[mesh].SetBuffers(mD3ddev)) return false;					
					for(mi=0;mi<mRenderQueue.mEffect[fx].meshInstBlended[m].nrInstances;++mi)				
					{
						instance = mRenderQueue.mEffect[fx].meshInstBlended[m].instances[mi];				
						if(mRenderQueue.mMeshInst[instance].visible)	// (MICRO)optimization? Sort per frame
						{
							if(!pD3dscene->PreSelectMeshInst(instance, mD3ddev)) return false;
							pD3dscene->mEffect[fx]->CommitChanges();

							RenderSubMeshes(pD3dscene, matid, mesh);					
						}
					}
				}
			}
			pD3dscene->mEffect[fx]->EndPass();
		}
		pD3dscene->mEffect[fx]->End();
	}
	return true;
}

/**************************************************************************************/
/***							RENDER SUBMESHES									***/
/*** ==> usage: within renderscene, to render submeshes with specific material		***/
/*** ==> renders all submeshes of given mesh with corresponding material			***/
/**************************************************************************************/
	
void CD3d::RenderSubMeshes(CD3dscene *pD3dscene, int pMatId, int pMeshId)
{
	for(sub=0;sub<mRenderQueue.mMaterials[matid].meshes[mesh].nrSubMeshes;++sub)		// NOT OPAQUE/BLENDED SPECIFIC
	{
		submesh = mRenderQueue.mMaterials[matid].meshes[mesh].submeshes[sub];
		pD3dscene->mMeshes[mesh].RenderSubMesh(mD3ddev, submesh, LIST); 

		#ifdef _DEBUG 
		++mDrawCallsPerFrame;
		mDrawTriPerFrame += pD3dscene->mMeshes[mesh].mSubMeshTable[submesh].FaceCount;
		#endif
	}
}

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Advertisement

Hi Niello,

I've overseen something and would like your advice..

What I do now is, for split-up opaque or blended:

- loop through all materials

- loop through all opaque/ or blended meshes

- loop through instances

- if visible continue and do the draw call

This works 80/20 (80% ok), because I now loop through meshes with don't use that material.

I've thinking about it and started to make an index per material, for meshes that use that material.

This might work, but on second hand, will still make me loop through meshes and instances way more then necessary.

So I though of the following solution:

Keep the loop the same but do a check if the mesh uses the material:

- loop through all materials

- loop through all opaque/ or blended meshes

- NEW: only select mesh (buffers) and continue to instances if mesh uses the material

- loop through instances

- if visible continue and do the draw call

This will return 'number of meshes' * material if statements on the CPU, reducing potentially hundreds to thousands unecessary setting of vertex/index buffers and instances (world/ worldinvtrans matrices).

So definitely a big win compared to the one extra if statement for each mesh/ material combination.

What would you advice, any easier/ more profitable solutions?

Update;

I definately need to use my notepad more often to post replies to myself :)
Fixed it already, made a mesh instance list per mesh blended and per mesh opaque.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Short update;

- implemented point lights with success

- changed from boundingsphere culling (Frustum) to boundingbox, big improvement

(will go to flexible later based on length/width of the mesh)

Here's a nice screenshot, with all types of light sources in action (ambient, diffuse and point):

lightingcombi.jpg

Next steps;

- implementing a lighting system and spatial culling/ areas.

Will go for this with my own ideas instead of tutorials and see what comes out.

Dividing the scene/ world in cubes (Static) and do bounding box check on those cubes. All meshes and lights will be part of a 'cube' and rejected early if the cube is culled. Maybe go 1 or 2 levels 'deeper' to have smaller cubes and earlier rejections. Is this the same principal a quadtree follows?

Another 'challenge' is to find out if it's possible to tell my effect/ shader how many point lights I have and process those

(sort of dynamic array and for loops in the effect). Not sure though if this is possible with VS/PS 2.0 (keeping max lights 8 into account).

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

This topic is closed to new replies.

Advertisement