A First Step in a Terrain Rendering Implementation

posted in mittentacular
Published August 14, 2007
Advertisement
My first real order of business once I returned from my weekend-long trip to the Michigan hinterlands where my permahouse resides was to start researching a decent terrain rendering algorithm for which my programming efforts would be focused upon for the next week or so. This was a process which I was firmly expecting to result in some sort of "bastard algorithm" based heavily on Thatcher Ulrich's Chunked LOD while drawing from my own experiences with ROAM 2.0 and Geomipmapping which I detailed in my article in Graphics Programming Methods. Much to my surprise, though, I had some well-respected graphics programming folks like ViLiO recommend Hugues Hoppe's research into terrain rendering with geometry clipmaps. The reason this is surprising to me is not because I felt Ulrich's Chunked LOD ideas were fantastic beyond improvement, though I did like his work, but I do feel it required a great deal of work just to implement and left a lot of room for improvement to get executed properly without much CPU intervention... But because I remember reading Hoppe's work into terrain rendering with geometry clipmaps back in his 1994 paper on the topic.

What I was not aware of was that Hoppe also wrote an absolutely fantastic article with Arul Asirvatham which discussed this same terrain rendering with a very heavy emphasis on practical use of the GPU using shader model 3.0 (namely, the use of vertex textures). After skimming this GPU Gems 2 article I was firmly convinced that I need to implement the algorithm as soon as I could get around to it. So, over the course of the last couple days I've read over the GPU Gems 2 article along with the original whitepaper upon which it was based several times each and also skimmed over the basic implementation done by FilouGK and I think I have come to pretty decent understanding of the algorithm.

One of the first things I usually do before undertaking any sort of non-trivial programming is to "sketch" (ie, write the code for) the class definition first. This is clearly a revolutionary first step, so take a moment to regain your composure at my genius.

But, yeah, I generally find that to be the only prerequisite for me when starting any project. I don't, as a rule, draw an excessive amount of pictures and diagram, I don't write pseudocode to detail the routines that I will, then, actually code, and I don't draw up unnecessary UML diagrams to detail how various data structures will interact with one another. Once I can look at a class definition and think "Yup," I just start coding and don't stop until I get something displaying on the screen which is, more or less, what I'm aiming for. Of course, that "first step" of getting things on the screen is far from a final product -- I program in a very incremental sort of process.

So, I apologize for the excessively dry nature of this dev journal entry. It has to do with a combination of my ridiculously tired state coupled with the fact that this is a journal entry was meant to lay the ground work for this terrain implementation which, I hope, will prove a decent tutorial on the topic as I go on. Anyhoo, here's the class definition as I have it so far.

class CTerrainNode : public ISceneNode{public:	~CTerrainNode( );	virtual HRESULT Create( CScene *pScene );	virtual void Release( );	HRESULT Init( CScene *pScene );	virtual HRESULT Update( float fTimestep );	virtual HRESULT Prerender( CScene *pScene );	virtual HRESULT Render( CScene *pScene );	virtual HRESULT Postrender( CScene *pScene );	void SetWorldViewProjMatrix( D3DXMATRIX matrixWorld );protected:	CTerrainNode( );private:	struct Clipmap	{		CTextureWrapper* m_pHeightMap;	//One Channel FP Height Data (DXGI_FORMAT_R32_FLOAT)		CTextureWrapper* m_pNormalMap;	//Four Channel Texture (DXGI_FORMAT_R8G8B8A8_UNORM)				ID3D10EffectShaderResourceVariable* m_pHeightTextureVar;		ID3D10EffectShaderResourceVariable* m_pNormalTextureVar;	};private:	//Algorithm Data.	Clipmap* m_pClipmaps;		D3DXVECTOR3 m_v3ViewerPos;						//Viewer position (Vx, Vz needed for block updates - Vy for active_region calcs).	unsigned long m_ulLevels;						//L	unsigned long m_ulGridSize;						//n (2^k - 1).	//M x M Block Geometry.	unsigned long m_ulBlockVertexRes;				//m (( n + 1 )/4)	unsigned long m_ulBlockNumVertices;		unsigned long m_ulBlockNumTriangles;	//M x 3 Block Geometry.	unsigned long m_ulRingNumVertices;	unsigned long m_ulRingNumTriangles;	//( 2m + 1 ) x 2 Trim Geometry.	unsigned long m_ulTrimNumVertices;	unsigned long m_ulTrimNumTriangles;	//Outer Border of Degenerate Triangles.	unsigned long m_ulDTNumVertices;	unsigned long m_ulDTNumTriangles;		//Shader Variables		//Frame.	D3DXMATRIX m_matWorldViewProj;	D3DXVECTOR3 m_v3LightDir;			//Block.	(Per-Clipmap?)	D3DXVECTOR4 m_v4FineTextureOrigin;	D3DXVECTOR2 m_v2AlphaOffset;	D3DXVECTOR4 m_v4ScaleFactor;	float m_fZScaleFactor;	float m_fOneOverWidth;	//Direct3D Data.	CInputDescWrapper* m_pInputLayout;	CVertexBufferWrapper* m_pBlockVB;							//m x m Block.	CVertexBufferWrapper* m_pRingVB[TERRAIN_LRING_COUNT];		//m x 3 Ring Fix-Up.	CVertexBufferWrapper* m_pTrimVB;							//(2m + 1)*2 Interior Trim.	CVertexBufferWrapper* m_pDegenerateTriVB;					//Outer Degenerate Triangles.	CIndexBufferWrapper* m_pBlockIB;	CIndexBufferWrapper* m_pRingIB;	CTextureWrapper* m_pDiffuseTexture;	ID3D10Effect* m_pEffect;	ID3D10EffectTechnique* m_pRenderTechnique;	ID3D10EffectTechnique* m_pUpsampleTechnique;	ID3D10EffectTechnique* m_pNormalCalcTechnique;	ID3D10EffectMatrixVariable* m_pViewVariable;	ID3D10EffectMatrixVariable* m_pProjectionVariable;		//Vertex/Pixel Shader Handles.	ID3D10EffectMatrixVariable* m_pWorldViewProjVar;		//matrix.	ID3D10EffectVectorVariable* m_pViewerPositionVar;		//float2.	ID3D10EffectVectorVariable* m_pLightDirVar;				//float3.	ID3D10EffectVectorVariable* m_pFineTextureOriginVar;	//float4.	ID3D10EffectVectorVariable* m_pAlphaOffsetVar;			//float2.	ID3D10EffectVectorVariable* m_pScaleFactorVar;			//float4.	ID3D10EffectScalarVariable* m_pZScaleFactorVar;			//float.	ID3D10EffectScalarVariable* m_pOneOverWidthVar;			//float.	//Constants	static const LPCTSTR TERRAINNODE_INPUTLAYOUTWRAPPER_NAME;	static const LPCTSTR TERRAINNODE_BLOCK_VBWRAPPER_NAME;	static const LPCTSTR TERRAINNODE_RING_VBWRAPPER_NAME;	static const LPCTSTR TERRAINNODE_TRIM_VBWRAPPER_NAME;	static const LPCTSTR TERRAINNODE_DEGENTRI_VBWRAPPER_NAME;	static const LPCTSTR TERRAINNODE_BLOCK_IBWRAPPER_NAME;	static const LPCTSTR TERRAINNODE_RING_IBWRAPPER_NAME;	static const LPCTSTR TERRAINNODE_EFFECT_FILENAME;	static const LPCSTR TERRAINNODE_EFFECT_SHADERPROFILE;		//Algorithm constants.	static const unsigned long TERRAINNODE_GRIDSIZE_DEFAULT;friend class CSceneNodeManager;};


I probably won't be able to write another entry until Sunday or Monday, as I'm moving into a new apartment on Friday, so I'll pick this up again once I get settled.
0 likes 3 comments

Comments

mittens
Well, it took some work to piece-meal the post of this entry together. GDNet really didn't want to accept the source snippet. For whatever reason. The only important thing missing worth noting is the vertex structure used:
struct TerrainNode_Vertex
{
	short x, y;
	
	inline void Set( short xIn, short yIn )
	{
		x = xIn;
		y = yIn;
	}
};
August 14, 2007 11:13 PM
ViLiO
Quote: Original post by mittens
Much to my surprise, though, I had some well-respected graphics programming folks like ViLiO recommend Hugues Hoppe's research into terrain rendering with geometry clipmaps.
Well-respected? I think you must have me confused with somebody else [grin]
August 15, 2007 07:08 AM
mittens
I did not.

We have the same rating. Tee-hee. We must be soul mates.
August 15, 2007 12:28 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement