作者归档:Ian

RSS feed of Ian

最后更新于 .

OpenGL的图像处理功能不可谓不强大,但是本身却不提供文字的显示和处理能力,所以我们只有借助于第三方的库来完成这个功能。这里介绍windows下如何在OpenGL里完成中文(当然也包括其他单字节字符了)的正常显示。
这里介绍windows的一个API函数:wglUseFontBitmapsW() ,函数的声明如下:

WINGDIAPI BOOL WINAPI wglUseFontBitmapsW(HDC, DWORD, DWORD, DWORD);

网上很多人都说wglUseFontBitmaps()函数无法显示中文,其实我们只要用wglUseFontBitmapsW()就好了,这个函数即宽字节版本,这样就可以对双字节字符进行正确处理。不过在调用函数前需要将字符串转成宽字节字符,这里调用MultiByteToWideChar()即可。函数的参数说明如下:
第一个参数是HDC,只要知道调用wglGetCurrentDC函数,就可以得到一个HDC了,这是一个windows的显示设备号。
第二个参数表示第一个要产生的字符。
第三个参数表示要产生字符的总个数。
第四个参数表示第一个字符所对应显示列表的编号。假如这里填1000,则第一个字符的绘制命令将被装到第1000号显示列表,第二个字符的绘制命令将被装到第1001号显示列表,依次类推。
下面是显示中文字符的函数:
void drawCNString(const char* str)
{
    int len, i;
    wchar_t* wstring;
    HDC hDC = wglGetCurrentDC(); //获取显示设备
    GLuint list ...

最后更新于 .

在3D游戏中,碰撞检测算得上是最复杂也是最影响游戏效果的环节了,这里简单介绍一下如何在OGRE引擎里实现简单的碰撞检测。

话不多说,先贴代码:


bool EdenCollisionManager::Collide()
{
bool collide=false;
m_KeyObject=*(m_ResearchObject.begin());  // m_Keyobjects是待检测的物体(游戏角色)
Ogre::AxisAlignedBox AABB = m_KeyObject->getWorldBoundingBox(true);  // 获取包围盒
m_AABBSceneQuery->setBox(AABB);  // 以包围盒建立场景查询
m_AABBSceneQuery->setQueryMask(ENTITY_QUERY_MASK); // 设置查询标识
Ogre::SceneQueryResult& results = m_AABBSceneQuery->execute();  // 获取查询结果
Ogre::SceneQueryResultMovableList::iterator iter = results.movables.begin();
if(results.movables.empty())
return false ...

最后更新于 .

这两天整理电脑里一堆乱七八糟的东西,无意中翻出了本科时和室友一起做的一个小游戏,重温了一下,发现还是蛮有意思的,决定把它写下来,算是一篇开发回顾吧。

这是游戏截图:

1

额,游戏不是原创,而是来源于原来上课时经常玩的三星手机上的一个游戏,后来因为我们太有爱又太无聊了,所以就自己写了一个。

游戏虽然小,但是从框架设计到UI、音效、评分系统,全部都有了,称得上是麻雀虽小五脏俱全。

其实无论从代码上还是最后的作品上来看,这个游戏都没啥值得骄傲的,但是即使过去几年了我和小猪偶尔提到这个作品还是会洋洋得意一番,在我看来,再某种程度上,这几乎算的上是自己做过的最好的一个项目了(当然本人也没多少开发经验),且不说敏捷开发的过程,这是我那时做过的不多的作品里唯一一个彻底贯彻了“前台界面和后台逻辑分离”这一原则的,除了coding之前的简单设计和中途的简单调整之外,我们两人的工作几乎没有交集,我只负责游戏的逻辑,小猪承包了所有的界面工作(最后一个晚上我看着小猪通宵的PS游戏里需要的图片素材,几乎是一个像素一个像素的修改,做美工果然很累啊),直到最后两个模块完美的结合在了一起,那时有一种酣畅淋漓的感觉。所以项目的架构绝对是第一重要的。

下面是源码的下载地址,有兴趣的可以去看一下:http://download.csdn.net/source/2228978

最后更新于 .

这一章介绍如何创建一个纹理采样器(Sampler)并应用到一个Shape上。简单的来说,纹理就是一个图像(一个像素信息的2D数组),这个图像可以应用到3D物体的表面。O3D中Texture的图像支持TGA、JPEG、PNG、DDS格式。使用Texture Sampler我们可以设置参数以说明如何将Texture贴到物体表面。

关于Texture

O3d用u表示Texture的水平坐标,用v表示其垂直坐标,坐标(u,v)的取值从(0.0,0.0)到(1.0,1.0),如下:

o3d_3_1

Texture Samplers

Texture Sampler封装一个texture object的指针和一系列状态参数(states),这些参数用于定义texture bitmap如何贴到表面。Sampler status可以通过定义Sampler object或者直接指定Sampler类的方法来设置。Sampler类定义了如下属性(括号里是默认值):


  • addressModeU (WRAP)


  • addressModeV (WRAP)

  • minFilter (LINEAR)

  • magFilter (LINEAR)

  • mipFilter (POINT)

  • borderColor ...

最后更新于 .

然后最痛苦的时候来了,尽管已经了解并掌握了几乎所有必需的知识,对于一个任务,我们能从功能上比较完美的实现,但这个时候,我们已经站上一个高度,追求完美的品质告诉我们一定要把界面做得尽量美观,让人过目难忘或者心情舒适。但开发过程中我们发现这是一个很痛苦的过程,一切并不像想象中的那么简单,自己做出来的界面经常会让自己反胃!我写过小游戏,写过播放器,但自己几乎不会去用它们,为什么?就是因为做出来的界面让自己实在难以提起兴趣,它们跟那些软件相比,差得太远。

我们很快就发现这个阶段遇到的困难,跟以前不一样。以前的难题可以通过经验和知识来克服,这次却不行了。我们之所以感到痛苦而不是困惑,就是因为问题不是出在专业技能上,这是审美的问题,这是大多数程序员缺乏、甚至是匮乏的素质。在程序员的世界里,大家都很容易陷入代码的泥淖而不自觉,这对于后台开发人员无所谓,但对于一名前台开发人员,一名从事界面开发的专业人来说,审美素质是很重要的,当这些人沉浸到代码和功能中时,那就是一场灾难!在界面的开发过程中,我们必须拿出大量时间来思考关于美学的东西,尽管这会中断你的工作,对于敲代码的程序员来说这很困难的,因为一旦开始了Programming,直到遇到难题为止,高效的节奏总是让人难以停下来。但适当的时候我们还是应该停下来,到了一定阶段,只有思考才可以让自己继续提高。

忘了从什么时候开始,在写界面的代码之前我总是喜欢先设计一下。当然我并不是那么专业的美工,我只是拿个画图工具把界面勾画一下,这里用什么控件,那里用什么控件,颜色该如何搭配……我们应该通过这个步骤,尽量把自己的美学融入设计之中,这样才能减少开发过程中的困惑和中断 ...

最后更新于 .

经过在第一阶段的痛苦磨炼,我们总算是打下了比较坚固的基础,可以快速的开发各种常见的窗口程序,各种控件的应用比较熟练,也能利用消息机制完成各种需要的功能。但一个完整的窗口程序不仅仅是由一堆简单的控件堆砌而成,在很多场合下,控件的事件响应函数需要调用具体的、功能性的代码。打个比方,尽管你可以使用如CPicture这些MFC提供的控件为你的窗口添加一些图片,但老实说,你并不知道这个控件中到底是怎么添加图片的,我发现,大多数情况下我们都是在窗口的属性编辑器里添加自己需要的属性,很少自己手动的添加一些功能性的代码。这个阶段我们的程序的一个明显的特点就是没有属于自己的控件,我们从来不会用面向对象的方式来开发自己的控件。

因为这样,我们可以做的事情很少。例如,窗口程序中,最常见的就是添加各种图片元素,复杂一些的如flash,此外还有一些其他的多媒体元素,如让窗口播放视频,显示OpenGL等的3D实时渲染结果,自定义绘制各种图表等。如果不能做到这些,我觉得就算不上真正懂了界面开发的技术,因为提到的所有这些,都关系到界面开发中最重要的技术之一,那就是GDI。GDI是每一个窗口程序都不可或缺的技术和元素,没有它,你的窗口不免功能单调、有形无神。

我曾在这些问题前徘徊了很久,尽管有这样或那样的教程,但大多杂乱无章,而且网上的资料看多了你就会发现几乎所有的教程追根朔源都引自有限的几个版本,就是这几个版本,也只是机械的调用API,你拿来后经常会出错,往往是因为这些作者也没有弄透彻。

学到了这里,去理解GDI的技术,就是迫在眉睫的需要了,如果不突破GDI的封锁,3D渲染输出、图表绘制、游戏开发这些复杂功能我们永远也理解不了,我们也永远不会知道各种不同格式图片之间的区别,例如bmp图片是用的或运算,而png图片用的是与运算 ...

最后更新于 .

一直纠结于究竟该用什么标题:总结其实说不上的,毕竟自己经验也不怎么丰富,充其量也就是一小结;换用感悟之类的也不大合适,这会让人以为我故作老成、经验丰富,诱骗大家,知根知底的朋友们当然很容易就揭穿我了。想来想去,虽然以后还有很长的路要走,但就暂且用“总结”来回顾一下自己做界面开发的一些经验和心得吧。
第一语言是C++,所以当初选择学习MFC似乎是理所当然的了。这是我最开始上网找资料以决定学什么时认定的道理,那时以为这是当然的嘛,可在过去这么长时间后,回头想想,甚是为自己的盲目而汗颜。我不是说MFC不好,在我看来,MFC是十分强大而优秀的,因此才会成为VC开发人员的首选。它的框架之庞大复杂,虽然使得其功能强大,而且拓展性强,但对于初学者来说实在太过晦涩,其架构和模版在简单明了的windows程序基础上加入了大量设计模式和其他常见的界面开发理念,如所见即所得和文档/视图模型,而这些东西,对于初学者来说是很难理解的。初学者固然可以通过模版向导来生成一个程序框架,也可以在窗口设计器里用鼠标拖拽出你的窗体,但除此之外呢?在相当长一段时间里,我只知道该怎么用MFC,但不知道为什么,经常性的觉得它的模版的架构是莫名其妙。
因为经常性的要用到,所以我经常为给程序加入某个功能而到处查资料,实现后却也只是一知半解,即使在我浏览过几遍精通MFC等书,依然无法为我答疑解惑,即使是最权威的《mfc windows程序设计》一书也是如此。我一度怀疑自己的学习方法,后来知道,问题的根本不是学习方法,而是学习顺序。提到的这些书 ...

最后更新于 .

上一篇我们调用O3D的createSphere函数绘制了一个球体,这一次我们将自己来完成这个几何体绘制过程,这将加深我们对O3D渲染过程的了解。

如同OpenGL绘制过程一样,在O3D中我们也需要定义3D模型的各个顶点坐标和连接类型(如三角形、四边形等),下面对具体的代码进行分析,这个代码将创建一个立方体。大家如果和OpenGL的代码做类比,可以发现有很多共同点。

首先还是一些初始化工作,这里的工作主要是创建primitives绘制图元。


var cubeShape = g_pack.createObject('Shape');
// 创建Primitive图元,这个图元里将包含创建3D物体(立方体)的几何数据(顶点)
var cubePrimitive = g_pack.createObject('Primitive');
// StreamBank用于存储顶点数据流
var streamBank = g_pack.createObject('StreamBank');

// 定义该图元的材质
cubePrimitive.material = material;
// 将Primitive和streamBank 指派给cubeShape
cubePrimitive.owner = cubeShape;
cubePrimitive.streamBank = streamBank;


下面初始化cube 的顶点数据:


cubePrimitive.primitiveType = g_o3d.Primitive ...

最后更新于 .

O3D是google公司开发的一套用于web3D开发的javascript API,是为了创建基于网页的3D图形操作界面和3D游戏而开发。网上经常有人把它拿来和OpenGL作比较,实际上,O3D可以看做是对OpenGL和D3D的封装。O3D的架构如下所示,底层是基于OpengGL和Direct3D接口实现,支持顶点shader和像素shader,支持GPU加速运算,因此,在不考虑带宽限制的条件下,O3D的渲染性能几乎可以媲美很多本地应用(譬如游戏),大家可以在google O3D的官网上找到不少很炫的Demo。o3d0

O3D目前主要支持Firefox, Safari, Google Chrome, Internet Explorer,当然,要想体验或者开发基于O3D的应用,必须先给自己的浏览器安装O3D插件。插件的下载地址为:http://tools.google.com/dlpage/o3d/eula.html

O3D的官网地址:http://code.google.com/intl/zh-CN/apis/o3d/,在这里可以找到很多文档。

另外,如果有兴趣,可以在google的svn服务器上找到O3D的所有代码来研究,地址为:http://o3d.googlecode.com/svn

介绍完O3D ...

最后更新于 .

今天网上看到这么一个问题:

int array[5]= {}; sizeof(array)=?

int Fuction(int a[])
{
return sizeof(a);
}
Fuction(array)=?

很有意思的问题,大概很多人会认为结果是20、20,但其实正确的答案是20、4,也就是说,函数Fuction里的sizeof求值结果为4,这应该就是int*的字节大小,看起来sizeof求的是数组a[]的首指针字节大小。奇怪的地方也就是这里,一个在函数外面,一个在函数内部,sizeof的参数完全一样啊,为什么结果却完全不同呢?
这里大家对sizeof的用法大致了解,其实出现这个结果的原因不在sizeof,当sizeof传入的参数是数组时,其结果的确是该数组占内存的大小,不过,c++中函数有一个特性:
数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址。
也就是说,在int Fuction(int a[])中,编译器认为传入的参数不是数组,而是把数组a[]的首地址指针传进去,所以这时sizeof ...