• About
    • Resume
A Game Developer also plays some guitar

Author Archives: Benny Chen

A game developer, a music lover, and a partisan fan of F.C. Barcelona

The First Signing – Maxwell delighted at Barca

July 18, 2009 3:17 pm / Leave a Comment / Benny Chen

2009-07-17_presentacion_maxwell_10In his official presentation to the media, Maxwell, Barca’s new left back has said that “today is a very special and happy day for me … it is a dream come true.”

马克斯维尔,作为巴萨最新引进的左后卫,在他的官方媒体见面发布会中说道:“今天对于我来说,是特殊而开心的一天,因为这是我梦想成真的一天。”

Serious, relaxed and humble, the first new addition to the treble winning squad, Maxwell, was officially presented today in Sala París at the Camp Nou. In his first official statement since signing, he said: “today is a very special and happy day for me. It is a dream come true. This is the strongest team in Europe”.

马克斯维尔作为三冠王巴萨在今夏的第一个增援,以一种认真、放松和谦虚的态度,于今日在诺坎普的Sala París正式亮相。在签约之后的第一次官方发言中,他说道:“今天对于我来说,是特殊而开心的一天,因为这是我梦想成真的一天。巴萨是欧洲最强大的球队。”

Four years, and option for one more

Before the presentation, Maxwell visited the offices to sign his contract, which will be for four seasons, with an option to stay for a fifth, depending on the number of games the Brazilian plays for Barça in his fourth year. The buy-out clause on the contract has been set at 50 million euros.

4+1年的合同

在见面会之前,马克斯维尔签订了一份4+1年的合同,第5年的合约取决于巴西人在前4年总的出场次数,合同的违约金高达5000万欧元。

Praise from Txiki

Barça’s director of football Txiki Begiristain, who attended the press conference along with president Joan Laporta, explained why they had signed Maxwell. “We understood that it was a position that needed improvement. He is a Brazilian player, but has grown up in Europe and where there is a very similar philosophy to our own, at Ajax, and he has also played top level competition at such a team as Inter.”

When asked to describe his new acquisition, Txiki Begiristain had no doubt that “he is top class, has a good physique and plenty of skill”.

来自巴萨技术总监的赞扬

巴萨的技术总监贝基里斯坦,和主席拉波尔塔一起出席了新闻发布会,他解释了为什么巴萨选择了马克斯维尔:“我们清楚我们的左边路需要加强 ,他是一位巴西人,但是他的足球生涯主要是在欧洲成长,他效力过阿贾克斯,他们的足球哲学和我们的非常类似,他还在国际米兰踢了很多顶级的比赛。”

当被问道如何评价他的最新引援时,贝基里斯坦确定的说:“他绝对是世界级的,身体和技术都很出色。”

His role

Maxwell has come to Barça just after Sylvinho’s contract ran out on June 30. “I know I am here to replace a player that has left and I know that competition for places at the club is a positive thing”, he said when asked about his role in the team, where he will be competing with Frenchman Éric Abidal for the left back place.

他的角色

马克斯维尔的到来是为了填补西尔维尼奥走后的空缺,后者的合同在6月30号已经到期。但是马克斯维尔还需要和法国人阿比达尔竞争左后卫的位置,在被问道如何看待自己未来在俱乐部的角色时,马克斯维尔说道:“我知道,我来到这里是为了替代一位已经离开的球员的位置,而且我认为,俱乐部里存在位置的竞争,肯定是一件积极的事情。”

Posted in: Visca Barça / Tagged: barca, Maxwell

诡异的画面

July 16, 2009 5:55 pm / Leave a Comment / Benny Chen

对shader文件做了一些修改,但还只是简单的渲染一个带纹理的盒子。出来的结果居然是这样的诡异,荧光闪闪,还带着雪花满盒子飘……

雪花bug

shader的错误的确很难查找,尤其是在编译通过只剩下逻辑错误的时候。最后错误的原因却又是如此的简单,我把PS的返回值写成了float,而应该是float4,我不小心漏掉了4,于是造成了这样诡异却绚烂的艺术效果,留文以作纪念。

Posted in: Computer Graphics / Tagged: pixel shader

To be improved

July 15, 2009 1:22 pm / Leave a Comment / Benny Chen

I tossed and turned last night, haunted by the sights of last Sunday’s football match. In that match, our team thrashed the opponent, which I am very proud of. Although there is still a tremendous space for our team to improve, we together have witnessed some progress, more or less.

However, to my disappointment, I was in terrible form in that match and missed an awful lot of chances. What a poor effort! These several days, I’ve been obsessed by sort of a strong sense of qualm.

OK, it’s true that this was the first time I’ve ever played a match in such a big pitch. So I was troubled with my stamina( I only played 30 minutes ) and maybe I was still a bit of nervous and lack of self-confidence. But they are all not reasons or excues for my unstable performance.

I still need more practice. And my tactical awareness and teamwork skill are both needed to be reinforced. Here are some advices that I summerized for myself:

  • Do not be too eager to pass the ball out. Stop and control the ball well first, and then find a teammate in good position to pass the ball. I think this is also my strength, so I should take better advantage of it.
  • Do not always try lob pass. It is not effective most of the time, a through pass is much better.
  • Never be scared to receive the ball. Run to a good position and be ready to receive a pass anytime with full confidence.
  • Even if I play as a winger, do not be too close to the sideline. Keep a proper distance from my mifield teammates, neither too far nor too close.
  • Take part in the defense more.

Next time, I hope I can contribute more to my team and gain a much better performance. And when can I break my goal drought? Wish that won’t be long^_*

Posted in: FCB BJ / Tagged: barca, Beijing, team

Some Tests

July 8, 2009 9:30 pm / Leave a Comment / Benny Chen

在最近的项目中,尝试进行了一些小实验,并记录了数据作为比较。实验机器的显卡是NVidia Geforce 8800 GT。

1. Skinning

skinning on CPU

                          skinning on CPU

skinning on GPU

                          skinning on GPU

从图中可以清晰的看出,对于骨骼蒙皮计算,CPU和GPU的差距可见一斑,GPU比CPU要快上10倍之多!这就是GPU并行计算的魅力!

2. Instancing & Stream Output

人物模型延用上面的这个模型(该模型差不多有1300个面),采用instancing技术渲染上千人(所有的Instance的动画在每一帧保持一致),并且实现了SO的版本,对它们的FPS进行比较。

Instancing without SO

   

     1,000 Instances              2,000 Instances             3,000 Instances

Instancing with SO

   

     1,000 Instances              2,000 Instances             3,000 Instances

因为所有的Instance的动画在每一帧都是一致的,所以如果不使用SO技术,则不得不对每个instance都需要进行一次蒙皮动画,这显然是一种浪费。Stream Output技术使得所有的Instance的蒙皮动画只需要执行一次,所以在效率上得到了一定的提高,如下表。

Number of Instances

FPS(Without SO)

FPS(With SO)

1,000

40

56

2,000

19

36

3,000

13

24

 3. Frustum Culling

换了一个低质量的模型(只有不到200个面),基本上万人级别是没有问题了,加上了Frustum Culling,则可以跑得更快!

使用3种方法实现了Frustum Culling,前两个都是基于CPU的,最后一个是基于GPU

  1. 顺序执行(最简单的对所有Instance遍历执行Frustum Culling算法)
  2. 四叉树(四叉树裁剪,我这里用四叉树用得有点牵强,因为人物不能移动且必须保持边长为2的N次方的阵型。这么简单的处理四叉树,只是为了做实验比较而已)
  3. GPU上的Frustum Culling(算法基本上跟1是一致的,只不过是在GPU上执行)

三个方法在同一个视角,使得它们截取和渲染的数量是一致的。总共65536个Instance,当前绘制都是15485个。

   

           顺序执行                              四叉树                             在GPU

效率比较如下表。

Current Rendering / Number of Instances
FPS
(Sequential Frustum Culling)
FPS
(Frustum Culling With Quad-tree)
FPS
(Frustum Culling on the GPU)
4388/16384 61 143 102
15485/65536 16 43 32

发现最快的是使用四叉树,而GPU的算法反而相对比较慢。原因很有可能是后者需要处理GPU和CPU的同步问题,在GPU进行Frustum Culling需要CPU从GPU读取Frustum Culling执行的结果(为了知道有多少个instance需要被渲染),在DX10的SDK文档里可清楚的写着,从GPU向CPU进行Map读取操作,是比较严重影响效率的,因为总有一方要停下来去等待另外一方的数据。

Posted in: Computer Graphics / Tagged: frustum culling, instancing, skinning

Get streaming output statistics

July 8, 2009 1:33 pm / Leave a Comment / Benny Chen

有时候需要知道stream output了多少个数据,这需要借助于DX10中的ID3D10Query,步骤如下:

  1. 创建D3D10_QUERY_DESC结构,设置D3D10_QUERY_DESC::Query为D3D10_QUERY_SO_STATISTICS,表明要调查的是SO的数据,设置D3D10_QUERY_DESC::MiscFlags为0;
  2. 通过ID3D10Device::CreateQuery()创建ID3D10Query;
  3. 用ID3D10Query::Begin()和ID3D10Query::End()函数包裹需要调查的SO代码;
  4. 通过ID3D10Query::GetData()获取SO的统计数据,填充在一个D3D10_QUERY_DATA_SO_STATISTICS

示例代码如下:

// 创建ID3D10Query
ID3D10Query *d3dQuery;
D3D10_QUERY_DESC d3dQueryDesc;
d3dQueryDesc.Query = D3D10_QUERY_SO_STATISTICS;
d3dQueryDesc.MiscFlags = 0;
m_pD3DDevice->CreateQuery( &d3dQueryDesc, &d3dQuery );

// ...... ......

// 统计SO信息
d3dQuery->Begin();
// ......draw something with SO......
d3dQuery->End();

// ......最好在这里放置一些代码,填置CPU的空闲......

// 获取SO信息
D3D10_QUERY_DATA_SO_STATISTICS soData;
while ( S_OK != d3dQuery->GetData( ( void* )&soData, sizeof( soData ), 0 ) );

值得注意的是,GetData一定要用while包裹,因为ID3D10Query从GPU获取数据是异步的(ID3D10Query继承自ID3D10Asynchronous)。这也是为什么最好能在GetData之前放置一些代码,因为CPU要等待GPU的数据,不如让CPU先去做些其他的事情,不要把CPU浪费在毫无意义的while循环等待上。

Posted in: Computer Graphics / Tagged: D3D10_QUERY_SO_STATISTICS, ID3D10Query, stream output

ConstructGSWithSO

July 1, 2009 5:07 pm / 1 Comment / Benny Chen

如果要使用SO(Stream Output),则在shader中必须使用ConstructGSWithSO函数来构造SO的GS,ConstructGSWithSO有两个参数。

  • VertexShader/GeometryShader – shader变量,因为SO的数据可以来自GS,也可以来自VS(如果GS为NULL),所以该参数可以是一个VertexShader,也可以是一个GeometryShader。(shader变量一般通过CompileShader函数获得)
  • Semantics – 描述SO的数据的semantic,该semantic须与上一个参数(shader变量)的shader函数的输出相一致。这个参数的格式要求比较变态,所以举例子说明比较好。

现有一个VS如下,需要SO它的输出数据:

struct VSInput
{
    ……(略)
};
struct VSOutput
{
    float3 pos : POSITION;
    float2 tex : TEXCOORD;
};
VSOutput VS VSInput
{
    VSOutput o;
    ……(略)
    return o;
}

那么ConstructGSWithSO函数应该这么写:

GeometryShader gsSkinningSO = ConstructGSWithSO( CompileShader( vs_4_0, VSSkinning_SO() ), “POSITION.xyz; TEXCOORD.xy” );

看粗体部分的第二个参数,首先是双引号的字符串形式的,双引号内逐个列出了VSOutput的semantic,每个semantic同时要用.xyz,.xy这样的形式标明出它的维数(特别注意,如果只是一个float或uint的一维数据,同样也需要用.x来标出它是一维的!)。各个semantic之间用分号隔开,但注意,最后一个semantic后面不需要加分号(不然创建shader失败!)。

Posted in: Computer Graphics / Tagged: ConstructGSWithSO, DX10

关于对std::vector的遍历

June 30, 2009 11:40 pm / 1 Comment / Benny Chen

static

上图是通过Instancing渲染了10000个低精度模型(低于200个面),有skin动画,但是人物没有AI。在实验室Geforce 8800GT的显卡上fps可以跑到80帧。

接着,我给人群加上点简单的AI,每个人物进行向一个目标点移动,于是我在每帧更新的时候添加了如下的这些代码。代码中,MeshInstance是instance的类,对应于一个人物实例,Move是移动人物实例的简单AI函数。对于所有的Instancing数据,我使用一个vector列表存储——m_vpInstancingData。代码通过vector的iterator(迭代器)遍历所有的instance,对每个instance执行Move函数。

for( vector< MeshInstance* >::iterator i = m_vpInstancingData.begin(); i != m_vpInstancingData.end(); i ++ )
{
    ( *i )->Move();
}

结果,加上这段代码之后,程序的效率居然骤降,如下图,fps只剩下44帧。这让我很是纳闷,因为在加上代码之前,CPU基本上是空闲的,因为所有的骨骼蒙皮+渲染全部都是GPU扛着,而在CPU加上一个10000次的for循环后,整体效率大打折扣。它的杀伤力有这么大么……CPU不太可能这么低能。

1

然后,我把(*i)->Move()这行代码注释掉了,仍然只有40多帧,即一个只是10000次的空for循环,仍然是效率的瓶颈,10000次的Move根本不是问题。

难道是迭代器在影响效率?于是把代码改成了下面这样,不用迭代器遍历vector,而直接使用数组形式访问vector来遍历。

for( int i = 0; i < NUM_INSTANCE; i ++ )
{
    m_vpInstancingData[i]->Move();
}

再次执行之后,fps又回归80帧!!

2

对于vector的遍历,一直以来一直都是通过迭代器遍历,但对于大型vector它居然会如此的影响效率,也是到今天才刚发现。但是STL的设计本来就是奔着方便高效的啊,迭代器不至于效率影响这么大吧,可能与Debug模式有关。于是,我做了一个小实验,代码如下。

#include <iostream>
#include <vector>
#include <time.h>

using std::vector;
using std::cout;
using std::endl;

#define MAX_NUM 1000000

int _tmain(int argc, _TCHAR* argv[])
{
    vector< int > vIntList;
    for ( int i = 0; i < MAX_NUM; i ++ )
    {
        vIntList.push_back( i );
    }

    clock_t start, end;
    double duration;

    start = clock();
    for ( vector< int >::iterator i = vIntList.begin(); i != vIntList.end(); i ++ )
    {
        ( *i ) ++;
    }
    end = clock();
    duration = ( double )( end - start ) / CLOCKS_PER_SEC;
    cout << duration << "seconds" << endl;

    start = clock();
    for ( int i = 0; i < MAX_NUM; i ++ )
    {
        vIntList[i]++;
    }
    end = clock();
    duration = ( double )( end - start ) / CLOCKS_PER_SEC;
    cout << duration << "seconds" << endl;

    return 0;
}

在Debug模式下执行的结果
debug

Release模式下
Release

可见,在Release版本中,它们几乎是一样快的。而在Debug版本中,可能因为迭代器需要额外的很多检查工作,所以比数组形式访问慢了很多很多……所以,对于采用哪种方式对vector进行遍历,效率怎样,如果最终是要发布为release版本的,那么这个问题大可不必担心。

Posted in: Computer Graphics, Some Experiences / Tagged: vector, 数组, 迭代器, 遍历

关于D3D10_MAPPED_TEXTURE2D的RowPitch

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

当对一个ID3D10Texture2D进行Map操作时,会遇到D3D10_MAPPED_TEXTURE2D结构。该结构有一个属性是UINT RowPitch,如果没有很好的理解这个属性的含义,Map操作的结果很有可能是不对的。

这是DX10 SDK文档对RowPitch的解释:

The pitch, or width, or physical size (in bytes), of one row of an uncompressed texture.

一个普通texture一行的字节总数就是它的RowPitch。但要特别注意的是:RowPitch并不就等于Texture2D的width乘以其每个纹元(texel)的字节数,即:

RowPitch ≠ width* sizeof (pixelFormat)

RowPitch总是大于等于后者,并且一般是等于一个2的n次幂。从上面也可以看出Pitch是以字节为单位,而width是以像素为单位的。

举例说明:

一个ID3D10Texture2D,创建它时所使用的D3D10_TEXTURE2D_DESC结构的Format属性是DXGI_FORMAT_R32G32B32A32_FLOAT,即一个纹元占16(4×4)个字节,Width属性是400,即每一行有400个纹元,则可计算每一行16 * 400 = 6400bytes。但如果对Texture2D进行Map操作时,可以发现,Map后所得到的D3D10_MAPPED_TEXTURE2D结构的RowPitch的值却是8192(是大于6400的最小的2的n次幂)。

所以在进行Map操作时,需要针对RowPitch,而不要依赖定义texture时的width。

但是,在fx文件中对纹理进行采样的时候,针对的则是width,见如下fx代码。其中offset是相对于起点的偏移量,g_TexWidth是一个二维纹理的width,可见为了获得offset在纹理中的uv坐标,计算都是相对于width的,这时不用考虑pitch。

	
uint baseU = offset % g_TexWidth;
uint baseV = offset / g_TexWidth;
Posted in: Computer Graphics / Tagged: D3D10_MAPPED_TEXTURE2D, ID3D10Texture2D, RowPitch, width

std::bad_alloc?

June 29, 2009 5:34 pm / Leave a Comment / Benny Chen

今天遇到一诡异的Bug,在某段代码之后,只要一执行vector的push_back函数,就报出异常——std::bad_alloc。

bad_alloc?这一般是new申请内存不足而扔出的异常么,内存耗光?绝对不可能。

折腾了很久,最后发现的错误根源是如此的愚蠢。

对于类的一个成员变量 QuadNode *m_pChild[4],我对它的初始化居然是这样的:

ZeroMemory( m_pChild, sizeof( QuadNode ) * 4 );

很显然,sizeof( QuadNode )绝对是大错特错,因为m_pChild是一个指针数组,而不是QuadNode的数组。

其实这个错误不值得拿出来一说,只不过是Bug所表现出来的症状有些奇特,所以留文以作警示。

Posted in: Some Experiences

安息吧, Michael

June 26, 2009 9:11 pm / Leave a Comment / Benny Chen

早晨一睁眼,打开电脑,欲查看昨夜联合会杯另一场半决赛的结果,然而,铺天盖地而来的,是杰克逊的死讯。

我完全惊呆了,原本还处于惺睡状态的大脑,一下子轰鸣而醒。到底发生什么了,我几乎不敢相信这是真的。这则恐怖的消息,正如他的专辑名《Thriller》那样,令人毛骨悚然。

震惊之后,是欲哭无泪的悲伤。

我来自一个小城镇,在那样一个消息闭塞的地方,在还没有网络的时代,能够接触到的西方摇滚乐歌手,几乎就是杰克逊这样天王级别的了。我无法忘记,初中的时候,家里的一盘杰克逊MTV的VCD,我守在电视前,看了一遍又一遍,如斯疯狂。后来,一盘《Off The Wall》的卡带,几乎伴随了高中3年的时光。那是他79年的第一张专辑,也是我最爱的一张,远远更甚于他更有名的《Thriller》。后来那盘卡带不慎丢失了,到了大学时代,我甚至到处搜寻买了一张打口的正版《Off The Wall》。虽然大学时代才是我真正开始疯狂迷恋摇滚乐的开始,但杰克逊对我的影响是巨大的。他是我真正听到的西方摇滚乐的第一个声音。

他虽然不是我的偶像,但他是我偶像的偶像。也许,他那些年代的辉煌,60年代或70年代生的人会感触更深,对与80年代生人有些遥远。但就像离我更远的披头士也给过了我无数的感动,他的歌声,他的舞蹈,他的那些恐怖的销量,当之无愧的流行乐之王,还是会让你在多年后看到时,激动万分,唏嘘不已,感叹自己的生不逢时。

上天总是嫉妒这样的天才。摇滚之王Elvis Presley,死在自己的不惑之年;最伟大的摇滚歌者,我的绝对偶像,John Lennon,同样被人枪杀在不惑之年;而流行之王Michael Jackson,在自己的知命之年被夺了命。

天才是如此的短命,而他们的短命也更赋予了他们人生的传奇。今天,Michael的死,赋予了一个音乐传奇,也是一个音乐传奇的结束,一段伟大的流行音乐历史的终结。今天,流行音乐死亡了?或许吧。

《We are the world》,这是Michael在80年代唱给非洲的歌,而在今天,全世界,we are the world,为你的离开而陷入悲伤。

安息吧,Michael.

我们爱你。

Posted in: A Day in the Life / Tagged: Michael Jackson

Post Navigation

← Older Posts
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