• About
    • Resume
A Game Developer also plays some guitar

Category Archives: Ok Computer

This is only about computer, computer science.

复习了一下Frustum Culling

June 25, 2009 6:17 pm / Leave a Comment / Benny Chen

上次跟frustum culling的亲密接触还是两年前的事情,那时的一个游戏Demo里实现了quad-tree地形,并使用frustum culling显著减少三角形面的渲染。

catcher-in-the-rye两年前的游戏Demo:麦田里的守望者

这一丢就是两年了,最近的大规模人群渲染项目,逼得我再次对frustum culling发出了呼唤,凭着模糊的记忆,再把frustum的一些原理复习了一下,不用1个小时,我就重拾了frustum culling的相关核心概念和技术,并获得了新的理解。

这是我从两年前就开始膜拜的Chad Vernon(www.chadvernon.com)大大的一段话:

When we tell DirectX to render geometry, it will perform a culling test to see if a vertex is within the view frustum before rendering the vertex. DirectX will only render a vertex if it is within the view frustum. However, this test occurs after the geometry has been transformed and lit by the world and view matrices. In more complex scenes, thousands of vertices would be transformed just to be rejected by the frustum test in DirectX. We can speed up performance quite a bit if we implement a view frustum culling test prior to our render calls.

DirectX本身在其pipeline中就会对顶点进行culling test的,但这要在顶点被”顶点变换与光照”(Transform&Lighting)之后。Vernon在写这段话的时候,应该还是DX9的时代。在DX10的文档里也赫然写着:(Rasterizer Stage)the stage clips vertices to the view frustum,是在VS,GS这些之后才进行。

而自己手工实现frustum culling的好处,就是可以将大量的非可视的顶点在送进渲染管线之前就被拒掉~

下面的这条链接对frustum culling有比较基础而详细的介绍(这哥们爆了好多粗口……),同时进行了一系列的优化,这也让我对frustum culling有了更深的理解。里面所链接的那篇讲解如何构造frustum的文章,当我再次翻开它的时候,马上就从我大脑中的碎片中搜索并意识到,我两年前曾经读过这篇文章。记忆总是在某个似曾相识的环境下被突然的激活。

http://www.flipcode.com/archives/Frustum_Culling.shtml

另外DX10的时代早已来临,AMD的那篇March of Froblins的论文里,Frustum Culling和LOD已经全部是在GPU里进行了,通过了Geometry Shader的帮忙。在如今这个时代,貌似把任何运算转移到GPU,一切皆有可能。

打算最近把frustum culling相对于我目前所进行的人群渲染项目,在CPU和GPU都实现一个版本,并进行一些性能的比较。在我现在的项目里,估计实现后GPU的版本不一定就比CPU的跑的快,因为我的GPU已经承载了大量的人群渲染任务,而CPU到目前为止还基本是空闲的。

Posted in: Computer Graphics / Tagged: frustum culling

indexed triangle lists最快

June 16, 2009 5:30 pm / Leave a Comment / Benny Chen

一直以为,因为一条三角形带(triangle strip)会把处理与传输m个三角形的代价从3m个顶点降到(m+2)个顶点,所以它是最高效的。今天从Gamedev的一篇帖子才知道,索引三角形列表(indexed triangle lists)才是最快的。

确实,三角形带减少了输入显卡的顶点数,但对于现今的显卡来说,带宽早就不是问题了!

至于为什么三角形带是最快的,因为在处理顶点时它可以最大化显卡缓存的使用率(cache hit ratio)。

下面这段话摘自Tom Forsyth的论文Linear-Speed Vertex Cache Optimisation,如何分配三角形的序列,以使的cache得到最好的利用。算法是贪心性质的,速度可以达到O(N),有兴趣可以研究研究。

Indexed primitives have replaced strips and fans as the most common rendering primitive in graphics hardware today. When rendering each triangle, the vertices it uses must be processed (for example, transformed to screen space and lit in various ways) before rendering. Indexed rendering hardware typically uses a small cache of vertices to avoid re-processing vertices that are shared between recently-rendered triangles. Using a moderately-sized cache (anywhere between 12 and 24 vertices is common) reduces the load on the vertex processing pipeline far more than the older non-indexed strips and fans model – often halving the amount of work required per triangle.

Posted in: Computer Graphics / Tagged: 三角形列, 三角形带

ID3D10EffectPass::Apply

June 15, 2009 5:13 pm / Leave a Comment / Benny Chen

因为没有很好的理解这个函数的作用,而导致被一个Bug纠缠了半天。

这个Bug是这样的:渲染一个人物模型和一个地板模型,它们分别有不同的纹理,但渲染出来的结果却是——人物的纹理贴到了地板上,地板的纹理贴到了人物上,纹理错位了!

我明明在渲染前都分别将各自的纹理视图(ID3D10ShaderResourceView)设置到shader的纹理接口(ID3D10EffectShaderResource)上了啊,怎么会出现如此诡异的现象。

调的很崩溃,freaking me out…对于bug源的推理,我磨了好久,才想到了去打破我的思维定势——真正启动Shader开始执行的是Draw函数,所以对于渲染状态的设置只需要在Draw函数前设置就是有效的。

前半句没错,后半句,非也!

我忽视了Apply函数的作用,它不仅仅只是挑选某个pass而已。

这是DirectX SDK Document对ID3D10EffectPass::Apply的描述:

Set the state contained in a pass to the device.

将状态提交到设备。马上我就恍然大悟,我设置纹理的语句是在Apply之后进行的。把设置纹理的语句提到Apply函数之前,于是,纹理物归原主。

调试BUG,除了需要很好的逻辑推理排错能力,还要勇于去怀疑一些思维定势(当然也没必要怀疑一切),有时,或许越是深信不疑的某些东西就是错误的根源,打破它,错误也便迎刃而解。

before bug was fixed
before bug was fixed

after bug was fixed
after bug was fixed

Posted in: Computer Graphics / Tagged: Apply, DX10, ID3D10EffectPass

二叉查找树(Binary Search Tree)

May 13, 2009 3:03 pm / Leave a Comment / Benny Chen

今天下午闲着无聊,复习了一下“二叉查找树”,并用代码温习了一下。
对于二叉查找树,一般支持的操作有:查找关键字,最大值,最小值,前驱和后继等等的查询,对于高度为h的树,它们都可以在O(h)时间内完成。

BinaryTree.h

#ifndef __BINARY_TREE_H__
#define __BINARY_TREE_H__

template < typename Key, typename Data > class BinaryTree;

/*! 树的节点 */
template < typename Key, typename Data >
class TreeNode
{
public:
	//! 默认构造函数
	TreeNode()
		: m_pParent( NULL ),
		  m_pLChild( NULL ),
		  m_pRChild( NULL )
	{
	}

	//! 构造函数
	TreeNode( Key k, const Data & d )
		: m_pParent( NULL ),
		  m_pLChild( NULL ),
		  m_pRChild( NULL ),
		  m_key( k ),
		  m_data( d )
	{
	}

	//! 拷贝构造函数
	TreeNode( const TreeNode< Key, Data > & node )
		: m_pParent( NULL ),
		  m_pLChild( NULL ),
		  m_pRChild( NULL ),
		  m_key( node.m_key ),
		  m_data( node.m_data )
	{
	}

	//! 赋值函数
	TreeNode< Key, Data > & operator=( const TreeNode< Key, Data > & rhs )
	{
		m_key = rhs.m_key;
		m_data = rhs.m_data;
		return *this;
	}

	//! 获取键值
	Key GetKey() const { return m_key; }
	//! 获取数据
	Data GetData() const { return m_data; }
	//! 设置数据
	void SetData( const Data & d ) { m_data = d; }

private:
	Key			m_key;					//!< 关键字
	Data		m_data;					//!< 数据
	TreeNode	*m_pParent;				//!< 父节点
	TreeNode	*m_pLChild;				//!< 左子节点
	TreeNode	*m_pRChild;				//!< 右子节点

	friend class BinaryTree< Key, Data >;
};

/*! 二叉查找树 */
template < typename Key, typename Data >
class BinaryTree
{
public:
	BinaryTree()
		:m_pRoot( NULL )
	{
	}

	~BinaryTree()
	{
		while ( m_pRoot != NULL )
		{
			DeleteNode( m_pRoot );
		}
	}

	typedef TreeNode< Key, Data > _TreeNode;

	//! 查找
	_TreeNode * Search( const Key & destK ) const;

	//! 最小值
	_TreeNode * Minimum( _TreeNode * pNode ) const;

	//! 最大值
	_TreeNode * Maximum( _TreeNode * pNode ) const;

	//! 后继
	_TreeNode * Successor( _TreeNode * pNode ) const;

	//! 前任
	_TreeNode * Predecessor( _TreeNode * pNode ) const;

	//! 添加节点
	void AddNode( const _TreeNode * pNode );

	//! 删除节点
	void DeleteNode( _TreeNode * pNode );

	//! 获取根节点
	_TreeNode * GetRootNode() const { return m_pRoot; }

private:
	_TreeNode	*m_pRoot;	//! 根节点
};

template < typename Key, typename Data >
void BinaryTree< Key, Data >::AddNode( const _TreeNode * pNode )
{
	assert( NULL != pNode &&
		"pNode cannot be NULL" );

	_TreeNode *newNode = new _TreeNode( *pNode );
	_TreeNode *n1 = m_pRoot;
	_TreeNode *n2 = NULL;
	while ( NULL != n1 )
	{
		n2 = n1;
		if ( newNode->m_key < n2->m_key )
		{
			n1 = n2->m_pLChild;
		}
		else
		{
			n1 = n2->m_pRChild;
		}
	}
	if ( NULL == m_pRoot )
	{
		m_pRoot = newNode;
	}
	else
	{
		if ( newNode->m_key < n2->m_key )
		{
			n2->m_pLChild = newNode;
		}
		else
		{
			n2->m_pRChild = newNode;
		}
		newNode->m_pParent = n2;
	}
}

template < typename Key, typename Data >
void BinaryTree< Key, Data >::DeleteNode( _TreeNode * pNode )
{
	assert( NULL != pNode &&
		"pNode cannot be NULL" );

	_TreeNode * n1 = NULL;
	_TreeNode * n2 = NULL;

	//! n1是要删除的节点
	if ( NULL == pNode->m_pLChild || NULL == pNode->m_pRChild )
	{
		//! 没有或者只有1个子节点,删除的节点时pNode
		n1 = pNode;
	}
	else
	{
		//! 左右子节点都有,先删除pNode的后继结点
		n1 = Successor( pNode ); // 该后继一定没有左节点
	}

	//! n2是n1唯一的一个子节点
	if ( NULL != n1->m_pLChild )
	{
		n2 = n1->m_pLChild;
	}
	else
	{
		n2 = n1->m_pRChild;
	}
	
	//! 为删除n1所作的指针修改
	if ( NULL != n2 )
	{
		n2->m_pParent = n1->m_pParent;
	}
	if ( NULL == n1->m_pParent )
	{
		// Root是唯一的一个父节点为空的节点
		m_pRoot = n2;
	}
	else
	{
		if ( n1 == n1->m_pParent->m_pLChild )
		{
			n1->m_pParent->m_pLChild = n2;
		}
		else
		{
			n1->m_pParent->m_pRChild = n2;
		}
	}

	if ( n1 != pNode )
	{
		//! pNode左右子节点都有的情况,
		//! 用pNode的后继结点的关键字和附加数据替换掉pNode的关键字和附加数据
		pNode->m_key = n1->m_key;
		pNode->m_data = n1->m_data;
	}

	delete n1;
}

template < typename Key, typename Data >
TreeNode< Key, Data > * BinaryTree< Key, Data >::Search( const Key & destK ) const
{
	_TreeNode *n = m_pRoot;
	while ( NULL != n && destK != n->m_key )
	{
		if ( destK < n->m_key )
		{
			n = n->m_pLChild;
		}
		else
		{
			n = n->m_pRChild;
		}
	}

	return n;
}

template < typename Key, typename Data >
TreeNode< Key, Data > * BinaryTree< Key, Data >::Minimum( _TreeNode * pNode ) const
{
	assert( NULL != pNode &&
		"pNode cannot be NULL" );

	while ( NULL != pNode->m_pLChild )
	{
		pNode = pNode->m_pLChild;
	}

	return pNode;
}

template < typename Key, typename Data >
TreeNode< Key, Data > * BinaryTree< Key, Data >::Maximum( _TreeNode * pNode ) const
{
	assert( NULL != pNode &&
		"pNode cannot be NULL" );

	while ( NULL != pNode->m_pRChild )
	{
		pNode = pNode->m_pRChild;
	}

	return pNode;
}

template < typename Key, typename Data >
TreeNode< Key, Data > * BinaryTree< Key, Data >::Successor( _TreeNode * pNode ) const
{
	assert( NULL != pNode &&
		"pNode cannot be NULL" );

	//! 2 cases
	if ( NULL == pNode->m_pRChild )
	{
		//! 1: 右子树为空
		//! 向上查找直到遇到某个是其父节点的左儿子的节点时为止
		while ( NULL != pNode->m_pParent && pNode->m_pParent->m_pLChild != pNode )
		{
			pNode = pNode->m_pParent;
		}
		return pNode->m_pParent;
	}
	else
	{
		//! 2: 右子树不为空,后继即为右子树中的最左节点
		return Minimum( pNode->m_pRChild );
	}
}

template < typename Key, typename Data >
TreeNode< Key, Data > * BinaryTree< Key, Data >::Predecessor( _TreeNode * pNode ) const
{
	assert( NULL != pNode &&
		"pNode cannot be NULL" );

	//! 2 cases
	if ( NULL == pNode->m_pRChild )
	{
		//! 1: 左子树为空
		//! 向上查找直到遇到某个是其父节点的右儿子的节点时为止
		while ( NULL != pNode->m_pParent && pNode->m_pParent->m_pRChild != pNode )
		{
			pNode = pNode->m_pParent;
		}
		return pNode->m_pParent;
	}
	else
	{
		//! 2: 左子树不为空,前任即为左子树中的最右节点
		return Maximum( pNode->m_pLChild );
	}
}

#endif

测试BST

#include "BinaryTree.h"

int num[11] = { 15, 6, 18, 3, 2, 4, 7, 13, 9, 17, 20 };

int _tmain(int argc, _TCHAR* argv[])
{
	BinaryTree< int, int > myTree;
	typedef TreeNode< int, int > Node;
	
	for ( int i = 0; i < 11; i ++ )
	{
		int x = num[i];
		Node n( x, 0 );

		myTree.AddNode( n );
	}

	Node *pRoot = myTree.GetRootNode();
	Node *min = myTree.Minimum( pRoot );
	Node *max = myTree.Maximum( pRoot );

	Node *someNode = myTree.Search( 17 );
	Node *pre = myTree.Predecessor( someNode );
	Node *suc = myTree.Successor( someNode );

	myTree.DeleteNode( suc );

	return 0;
}

参考资料:算法导论P151-P158

Posted in: Talking in Code / Tagged: binary search, tree

Post Navigation

 
Newer Posts →

LinkedIn

Milan Petrovic

Categories

  • In My Life (25)
    • A Day in the Life (8)
    • English Learning (2)
    • Learn a Word (7)
    • Something In The Way (8)
  • Music Heaven (8)
    • Guitar (1)
    • In Concert (1)
    • Lyrics (3)
  • OK Computer (54)
    • 3D (3)
    • C++ (10)
    • Computer Graphics (15)
    • Game Programming (23)
    • iOS (6)
    • Linux (1)
    • Lua (9)
    • My Projects (3)
    • Some Experiences (9)
    • Talking in Code (2)
    • Unity (2)
  • Quotations (2)
  • Uncategorized (1)
  • Visca Barça (24)
    • FCB BJ (5)

Recent Posts

  • [译]优化你的手机游戏(没有延迟的,才是健康的)- 一篇给游戏美术设计师读的文章
  • 新浪微博API for MOAI
  • 稍后继续
  • Unity Developer ++
  • Another Thread @ Moai Forum
  • 1st Day of Golden Week
  • 为SyntaxHighlighter添加新语言
  • 基于Lua的State Pattern
  • Class Diagram of Pacman
  • 基于Moai的Pacman

Recent Comments

  • 约修亚_RK on 为SyntaxHighlighter添加新语言
  • 爱装的小男孩 on 小心DLL链接静态库时的内存错误
  • happyfire on Game Loop的几种实现方式
  • William on 新浪微博API for MOAI
  • Benny Chen on 新浪微博API for MOAI
  • your man on 新浪微博API for MOAI
  • 你家男人 on 稍后继续
  • 逍遥 on 关于对std::vector的遍历
  • papa on Unity Developer ++
  • T客网 ︱ Techpot » Blog Archive » iOS开发与OpenGL ES相关问题整理(1) on iOS开发与OpenGL ES相关问题整理(1)

Tags

2d 3D 3dsmax 3ds max air Apply architecture Asia tour barca Beijing bilbao binary search blocked bob boost bruce springsteen C++ capo CGContextDrawImage Champions League Change DLL DX10 eval exporter flash framework frustum culling game game engine iniesta ios linux lua Moai opengles pacman plug-in plugin 北京 导出插件 崩溃 巴萨 游戏引擎 踢球
© Copyright 2026 - A Game Developer
Infinity Theme by DesignCoral / WordPress