2006年12月23日

Shadow Volume详解(3)

事隔几个月之后,终于有时间把代码贴上来了。代码下载
今天在搞Hardware shadow map,有一点小进展,但是还是有不少距离。期待写shadow map系列















1.先说一下3ds文件的结构吧,只说跟投影有关的东西,就是它是包含多个object的,读进来的模型也是包含多个object的。
// 模型信息结构体

struct t3DModel
{
UINT texture[MAX_TEXTURES];
int numOfObjects; // 模型中对象的数目
int numOfMaterials; // 模型中材质的数目
std::vector <tMaterialInfo> pMaterials;// 材质链表信息
std::vector <t3DObject> pObject; // 模型中对象链表信息
};
2.首先执行阴影的初始化工作
//载入模型
g_model.Import3DS(fileName);
// 初始化阴影
g_modelShadow.Init(g_model.GetModel(), g_lightPos);
以下是ModelShadow::Init的定义

bool ModelShadow::Init(const t3DModel &model,const float *lightPos)
{
// 获取object的数目
objNum_ = model.numOfObjects;
objInfoTable_.resize(objNum_);
for(int objIndex = 0 ; objIndex < objNum_ ; objIndex++ )
{
....
}
glClearStencil(0);
....
SetConnectivity(); // 设置三角形之间的链接关系
CalcPlane(); // 计算每个三角形的平面方程
....
}
3.初始化之后就可以渲染了
// 绘制模型,阴影
// 重设光源位置,如果光源位置不便可以忽略
g_modelShadow.ResetLightPos(g_lightPos);
// 这里获取当前的model view matrix(MVM)
g_modelShadow.GetCurMatrix();
glPushMatrix();
// 放置物体
glTranslatef(g_modelPos[0], g_modelPos[1], g_modelPos[2]);
// 这里又获取了一次model view matrix
g_model.Draw();
g_modelShadow.Draw();
glPopMatrix();
通过两次获得的矩阵的差异就可以算出中间做了那些变换,比如(glTranslatef(g_modelPos[0], g_modelPos[1], g_modelPos[2]); )
现在看一下ModelShadow::Draw的实现
void ModelShadow::Draw() const
{
glClear(GL_STENCIL_BUFFER_BIT);

GLfloat objMatrix[16];
glGetFloatv(GL_MODELVIEW_MATRIX, objMatrix);

// 求逆矩阵,这四条语句就是在求物体的局部坐标系里灯的坐标

InverseMatrix16(objMatrix);
MultiplyMatrix16(objMatrix, curMatrix_);
memcpy(lightPos_, orignLightPos_, 4 * sizeof(float));
Mtx16MultVect4(objMatrix, lightPos_);

//Cast the shadow
CastShadow();
}
再看ModelShadow::castShadow

void ModelShadow::CastShadow() const
{
SetVisibility(); // 设置每个三角形的可见性
....
// 第一趟渲染,通过模板测试的话模版值增一
glFrontFace(GL_CCW);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
DoShadowPass();

// 第二趟渲染,通过模板测试的话模版值减一
glFrontFace(GL_CW);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
DoShadowPass();
....
glStencilFunc(GL_NOTEQUAL, 0, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

//在全屏幕混合一张图
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0, 0, 0, 0.3f);
....
}
有一些问题,我在(2)里说错了的
glLoadIdentity();
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(-0.1f, 0.1f, -0.10f);
glVertex3f(-0.1f, -0.1f, -0.10f);
glVertex3f( 0.1f, 0.1f, -0.10f);
glVertex3f( 0.1f, -0.1f, -0.10f);
glEnd();
这个覆盖全屏真是投机取巧
gluPerspective(45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0);
如果那个角度比45.0f大就无法覆盖了
正确的做法是改用正投影。

Pictures of Blizzard Art Contest

孤独的兽人战士,半兽人?
貌似是巫妖王,嘴巴好夸张啊呵呵。
For the lich king.













Warrior of the night, assemble.


copyright
BLIZZARD-ART

One of blizzard's christmas flash


蛮温暖的呵,苦工老弟都开始玩了。

2006年12月14日

Words on:图形程序开发人员指南(一)

书的英文名是《Michael Abrash's classic Graphics Programming Black Book》。Michael Abrash是图形学的先驱者之一,《DOOM启示录》也有描述。云风的书(《游戏之旅-我的编程感悟》)上也多次提到了这本书。我摘录了一些,但是是非技术的。以后多贴点技术的。
在东校区图书馆无意间发现的,大家要到东校区借书的话来找我。:D

[Michael Abrash]        我想说的是我们现在日益进入一个紧密互联,高度逼真的空间,我希望那些人意识到3D编程的难度和潜能实际上比以前更大。关键在于要沉溺于已经实现的技术,必须不断奋斗,力争"用不同的方式,用更少的代码,编写出更好的程序。"不断学习,改变并尝试新的方法,直至生命的终结,这样就能为将来留下自己的烙印。
        当说到这种乐趣是我生活的目的时,我认为我并没有言过其实(当然也许有一点点),我很高兴这么多读者都有这种热情。
        要敢于尝试不可能的事情,知道什么是不可以的也是有用的知识-一些意想不到的成功可能会让你顿悟:多数不可能的事情其实是可能的。
        这十年是微机程序员的十年,我庆幸自己不仅能成为其中的一员,而且能够在历史的发展中留下自己的痕迹。但愿未来的十年依然充满兴奋和喜悦。

[John carmack]编程不是一个零和的游戏,教给你的同行一些东西并不会丢失它们,我与人分享我能够做到的一切时感到很高兴,因为我深深的热爱编程。一辆法拉利小轿车不过时一种物质享受,但做人最重要的是真诚

2006年12月1日

我为何而生

这是个很麻烦的题目了,但是是必须要思考的。跟家里人的想法有点不大一样,一冲动就写了。
罗素有一篇这个题目的经典文章:
[William Russell]对爱情的渴望,对知识的追求,对人类苦难不可遏制的同情,是支配我一生的单纯而强烈的三种感情。这些感情如阵阵飓风,吹拂在我动荡不定的生涯中,有时甚至吹过深沉痛苦的海洋,直抵绝望的边缘。
像我的Blog的题目一样,《伊尔的理想》,我是为理想而存在的吧。
以前是不想这些事情的,整天上学上学,不思考。改变我高中生活的是一本让我懂得思考的杂志。改变我的大学生活的是盖茨的那句话:
[Bill gates]每天清晨当你醒来时,都会为技术进步及其为人类生活带来的发展和改进而激动不已。
我希望可以用技术改变人们的生活,这是我来做技术的原因。不是为了变得更强,也不是为了生活。为了这个也失去了一些的。空余时间,课程成绩等等。但是那些付出在我看来是值得的,我能感觉到自己的存在,未来的一天,我可以让这个世界更美好。至于做图形,是因为我想做游戏,这跟我的经历有关。还有很重要的是,同时也有了伙伴。(跟我爱罗不一样wo,有存在的理由,有伙伴。:D)
Live for ideal,not just live for living.

2006年11月29日

尝试了一下gDEBugger

以前就装过这个东西,不过没怎么用。NVPerfKit的一个部分。
NVPerfKit要先卸载本机的NVidia显卡驱动(驱动,:D),NVPerfKit会替换那个驱动,这样它就可以在底层截取渲染操作的信息。控制面板里的NVidia developer control panel也可以用了。
今天碰到了问题,前几天把ModelShadow(模型的阴影渲染)重写了一下,在Nehe的框架下写的。现在要转到xophiix的框架下。两边的操作基本上完全一样,但是那个阴影死活不出来。sigh.

于是就试了一下gDEBugger.gDEBugger分了八栏:OpenGL function calls history,OpenGL State Variables,Call stack,Properities,performance graph,counter, performance Dashboard,Function Calls Statistics.今天主要用的四OpenGL function calls history和OpenGL State Variables.监视了一下GL_CULL_FACE,GL_CULL_FACE_MODE,GL_STENCIL_BITS这个个关系比较大的状态变量,没对比出什么区别来。

然后把OpenGL function call录制了一遍,这个功能真的很好。有一个红色的小球样子的按钮就是了。录完点右边的一个按钮就可以在浏览器里看到,我对比了一下阴影渲染部分的主要调用,真的是没有太大不同。

还有就是可以设置断点,在出现OpenGL错误或者接到NVidia GLexpert的报告时中断,还可以自行设置在执行某些OpenGL函数时中断。如果支持条件中断就更好了。发现了一个以前的小bug,一个错误的OpenGL调用。

还可以在性能分析(profile)模式下工作,这个我并不了解,用NVPerfHUD也许更好吧,不过NVPerfHUD是为D3D程序工作的.....。最后,可惜gDEBugger不是免费的,只能用30天。

下载NVPerfKit

PS:今天又发现一些新的功能,比如同步显示代码(类似VTune)和Shader编辑器。profile也比我想象的更强。

2006年11月26日

卡马克(John Carmack) 的blog


卡马克(john carmack)最近写blog了,id开始涉足手机游戏
一个很有趣的事情是:这个网站是一群火箭爱好者的地盘
新发布的一款是<<Orcs & Elves>>,有多种版本,NOKIA的好像要求N70以上。还蛮高的。以前好像有另一款产品叫做DoomRPG
想想罗梅洛,离开id之后也是去做手机游戏了

时代已经不同了。游戏的商业价值已经被完全重视起来。id可以在技术上成为leader,在市场上恐怕很难了。即便手机游戏这样的特殊市场,gameloft等厂商也已经占先了。id本次的开发平台选用了BREW和java,各有两种平台的高端和低端产品。
[carmack]:In hindsight, we made a strategic mistake at the start of O&E development. We were fresh off the high end BREW version of DoomRPG, and we all liked developing on BREW a lot better than Java. ...The high end version is really wonderful, with all the graphics, sound, and gameplay we aimed for, but when we went to do the low end versions, we found that even after cutting the media as we planned, we were still a long way over the 280k java application limit.
开来carmack仍然那么追求效果,不过现在手持设备的能力毕竟与桌面电脑有差距。
[carmack]:The download size limits are probably the most significant restriction for gaming on the high end phones. I don't really understand why the carriers encourage streaming video traffic, but balk at a couple megs of game media.

2006年11月24日

blogger居然又走出祭坛了

刚刚解封,不过心里总是有阴影:以后会不会再被封呢

2006年10月15日

不能只凭兴趣做事啊

强烈感谢豆豆,一句话让我明白很多。
最近发生很多不顺心的事,看似没有关联的事情,跟这个都有关系,以前的确太凭兴趣了。
1.项目组的进度滞后
2.电工实习
3.C语言的课设

[项目]:项目组的进度滞后跟这个有很大关系的。做一个工程的话并不是只coding就可以的。还有很多,比如文档编写,搜集资料,做实验,想方案,还有一些很零碎的工作。并且这些工作都是很重要的,总要有人做的。这个我该反省一下了,来组里之后,零碎工作基本上没怎么做。组里的人通常有这样的倾向:总只关心提高自己的技术,而对于项目整体并不是那么关心。或者说把除coding之外的事情看得都很低级。
其实以前我也是完全按兴趣做事的人,怎么说呢,有点自私,太过于爱自己了。其实有些别的概念同样很重要的,比如责任,responsibility。仅有兴趣是很难完成一项工程的,中间遇到的困难和工程里你没有兴趣的那部分会阻止你的,这个时候Sense of responsibilty就很重要了。
其实以前xbull也谈过这个问题的,他说有兴趣是好的,但是他并不怎么相信兴趣。

我到现在仍然很相信兴趣,我只是觉得没有它的话不行罢了,但是在软件工程中team的成员都只凭兴趣做事会带来灾难的。

未完,有点事,一会儿再写

2006年10月4日

Shadow Volume详解(2)

然后重设模板测试比较函数:glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
绘制阴影体的所有背向视点的面
也就是当片于通过模板测试时,相应的模板缓存区域的值减一。接着红色区域+棕色区域的那个四边形被渲染。但是这个四边形位于红色的三角形的背后的部分无法通过深度测试,那个区域片元的位置比三角形离视点更远。













左图中棕色区域的模板缓存相应位置的值减1。经过这两次渲染,模板缓存中不为0的地方就是右边的淡蓝色区域减左边的棕色区域。
那个区域就是棕色区域下面的三角形区域,也就是阴影区
实际上标准的做法是把场景渲染两遍的,这个下面我会翻译。现在使用的是NeHe的做法,直接给整个屏幕蒙上一个蒙板,非阴影区颜色不变,阴影区颜色加深。


glStencilFunc(GL_NOTEQUAL, 0, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

//draw a shadowing rectangle covering the entire screen
glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glPushMatrix();
//将当前矩阵load indentity后,绘制任意大小的矩形都将覆盖整个屏幕
//xophiix告诉我,我才想起来的
glLoadIdentity();
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(-0.1f, 0.1f, -0.10f);
glVertex3f(-0.1f, -0.1f, -0.10f);
glVertex3f( 0.1f, 0.1f, -0.10f);
glVertex3f( 0.1f, -0.1f, -0.10f);
glEnd();
glPopMatrix();
然后就可以恢复OpenGL状态机设置,绘制物体本身。

2006年10月3日

Shadow Volume详解(1)

利用每天夜里的一部分时间写一下,这也是热爱吧。主要是翻译reference(1)里的,也讲一下Nehe的实现。现在使用的还是z-pass方法,以后会把z-fail,Nvidia 的改进z-pass乃至soft shadow加进来。
感谢两个人的话:
DancingWind:
如果我能让读者在读我的书的时候节省一分钟,那么这个班上有60个同学,我就节约了一个小时。好的教程往往可以事办功倍。
Emilmatthew:让更多人受益。
虽然我能力所限做不出好的教程,但是会尽力的。
Reference:
(1).Volume Shadows Tutorial by John Tsiombikas
(2).Nehe OpenGL tutorial:lesson 27 by Nehe
Steps:

[1]原理

[原理]首先,讲一下阴影体原理

我会尽我所能的讲得清楚一些,但是我仍然希望读者队模板缓存(stencil buffer),深度(depth)有基本得了解。我自己得学习过程也遇到很多困难,所得到得经验就是要先把原理弄明白,而不是急于看别人写好的code。













这个图是很理想的,足够简单又能说明原理。黄色的点代表光源,红色的平面代表一个三角形的物体,紫色,蓝色,棕色的平面就是构成阴影体的面(实际上这三个面应该在被离光源的方向上继续延长的,现在还是继续使用它吧)。灰色的代表阴影投射到的平面。

我们一边渲染一边讲:
[首先把要绘制阴影之外的物体绘制],这也是一个原则阴影物体要最后被绘制,这样做的目的是为了获取深度信息。深度:depth也就是物体离视点的距离,深度信息通常用做遮挡判断。比如在三维空间里绘制了一前一后两个平面,就要通过深度信息来判断两个平面的前后位置,进而其中一个平面判断那些位置被遮挡了。绘制完成之后屏幕上每点的深度值都已经确定下来,然后使深度缓存不可写
比如:glDepthMask(GL_FALSE);(DX下有类似的做法)
但是允许深度测试:glEnable(GL_DEPTH_TEST);
测试函数为:glDepthFunc(GL_LEQUAL);
也就是说只有深度小于原象素的象素才会被接受。


[然后绘制阴影体]
先清空模板缓存:glClearStencil(0);
开启模板缓存测试:glEnable(GL_STENCIL_TEST);

设置测试比较函数为: glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
然后设置片元通过或者未通过模板测试时如何队模板缓存中的数据进行更改: glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);也就是通过测试时增1。
然后绘制阴影体的所有朝向你的面,阴影体是这样形成的,以光源为一个顶点,物体边缘的一条边为边的三角形无限延伸所形成的四边形(理论上讲应该是一个无限的,但实际上长用一个足够长的来模拟),本例中就是紫色和蓝色的面。容易发现,这两个面上的所有片元都可以通过测试
相对应位置的模板缓存的值都会加1。
但是相应位置的深度信息不变,因为深度缓存被设置为不可写。
结果如右图所示:淡蓝色的区域模板缓存值增1。

2006年10月2日

反省

现在项目遇到了很大的困难, 或者说有很大的危险,该反思一下了。本来昨天就该写的,惰性又发作了。
从来没想过要追求完美,拿到任务就开始做,做到能用的程度就停下来休息。其实有很多东西都可以做得更好的。惰性一直没有克服。
自私,从暑假实习开始,就开始研究一些项目用不到但自以为很cool的东西。没有大局意识,也没有软件工程的意识。也没有关心过识别方面的事情。
骄傲,OpenGL好像很久都没有进步了。始终无法以始为终,最近才开始给自己订计划。如果热爱的东西都做不好的话,那就不能忍了。好在有realdodo,C++方面还学了一些东西。
post:晚上又开了会,刘老师也说了很多鼓励的话。虽然后面还有很多事情要做,但是大家都有passion了,都有想象力了,还有团结,这才是最重要的。
唉,做错了的事就无法挽回啊。

2006年9月27日

被ACMer激怒了

昨天晚上去自习,很晚才回来。路上遇到一个ACMer(不是Sendoh)说:”你怎么去自习,不在Dian干活。”真的是不
能忍了,还是以前一起搞ACM的,虽然我半途而废。我觉得做ACM也不用这么嚣张吧,我以前看过一篇文章,还亲自打印
了,ACM图灵奖得主的演讲:The humble programmer
即便可以选择的话,也不再去搞ACM。来到这里有我的理由的:理想,朋友 ,归属感......。起码在这里过得更快乐
。这总不该遭到鄙视吧。
还是小熊更实在一些,评价更中肯一点,小熊说Dian对新人来说是个很好的地方,很多团队对新人的培养都不好,但
是有经验的人在这里没法拿太多精力来做研究,提高自己。其实小熊也来过这里面试,还跟刘老师谈过,但是考虑到没有
足够的时间就放弃了。小熊说即便来了,也会常去俱乐部的,毕竟感情深一些。我对南一这里也很有感情了。
小熊又说了,高调做事,低调为人。
也要做点研究了,下一个是:soft shadow
post:有点激动了,其实没那么严重的

2006年9月21日

小熊 && 开复 &&金戈

昨天去CLUB跟小熊聊了一会儿.很久以前就听Sendoh说过小熊,偶尔还会在gmail上聊聊天,只是没见过面。小熊是很强的人,很多方面很有见地,很重感情的人。昨天聊过之后回来想了很多,为什么要做graphics,以后怎么做下去都是要好好想想的。HUST做研究生做graphics的绝少,水平也不那么好,还要另找地方了,以前松松散散的等着保研呢。
Shadow Volume研究了快一个月了,但是小熊这个知道的的确比我多。SDK没有好好利用。NV,ATI,DX的SDK都是很早就下了的,还有暴多的DEMO。今天到小熊昨天给我看的网站看了一下graphics.cs.lth.se/research/shadows/
还有MIT的开放课件
国内地址(CORE)

MIT
国外大学的graphics的水平的确超越我们很多(虽然对国内的情况不是特别清楚)。隆德大学,苏黎士工学院,斯坦福,MIT,甚至chalmers(查尔姆斯技术学院)也是相当有实力的。
想去年来读大学的时候便是抱着”恐遂汩没,故决然舍去,求天下奇闻壮观,以知天地之广大。“(苏辙)的决心来的。还碰巧去GameDev.Net转了一下,也有不少收获呢。
写了一晚上代码了,只好看篇论文休息一下


今天是HUST Google camp开营仪式,开复先生来做了发言。很随和的人,重感情的人,有主见。kaifulee演讲的题目是《21世纪最需要的七种人》,着实精彩,值得一看。可惜前面搞的活动实在是又俗套又恶心。。。


今天组里开例会,也遇到一些问题。以后能全天候工作的人越来越少了。组长的管理方式还是比较人性化的。


最近有一个演讲,csdn搞的,演讲是由金戈(《代码大全》翻译者)来做的。也放到一起
同样学计算学科,是有两个职业方向的computer science researcher,software engineer.这两种当然是有很大不同的。他主要谈的是engineer,engineer当然更倾向于实践。然后讲了一些engineer所需要的东西,更kaifu lee的也有些相似。

2006年9月20日

期待已久的摘要功能终于实现了!!!

在周亮同学的热心帮助下,我终于有了期待已久的摘要功能。
blogger本身未能提供这一功能真是缺憾。
以前下载过一段代码加到模板里,但是那个摘要会隐藏整贴的内容,不爽。呵呵


点击查看全部内容....

2006年9月19日

地形生成

本来地形这个东东可以用柏林噪声之类的生成不错的东西,还有四叉树,但是现在情况比较特殊。组长希望:远处有山。所以做生成有点麻烦(其实也不麻烦)。现在是用生成好的bmp高程图。有专门的软件做这个事情。 World Machine,国外做的东西,有点复杂,但是很强大,也很好用。现在还在学习中。bmp文件其实并不很合适,那个头部的定义就够BT了。而且生成的图是24bit信息的,其实8位就够了,不需要那么高的精度。居然还有48位的,太。。。了,也许是因为专业吧。我用的还是trail版,pro简直难以想象了。
昨天的纹理问题今天解决的差不多了,还有法线一会儿也能解决。那就无敌了。可惜cactus同学嫌VSS服务器文件太大,把整个data目录给。。喀。。了,所以文理图不敢改,改了只有我这里是最新的,又会回到手动源代码管理的时代了。唉,得想点办法。

2006年9月17日

选择数据类型还是要注意一点

我想,如果不是必须的话,unsigned 还是少用为好
昨天晚上帮一个同学做课设,做一个简单的分词器,就是把句子里的词语划分出来。字典文件里的词条有116500左右。我是把整个字典都转化成pascal-like的格式存储到内存里。pascal-like格式好啊,字符串比较函数超级的快,因为长度是已知的,一定程度上也更安全。然后做了一个hash表,表里有内容为index的list指向字典的不同位置。失误就在于用了unsigned int 来表示这个,unsigned int 的最大值在65535,比那个116500小了一半,然后害死我了,always debugging......。这个问题倒也不怪unsigned,怪short长度太短。豆豆帮我用doxgen生成了文档,果然是很强大的工具呢,对Unix文化和工具越发有好感了。
今天绘制Terrain,又害了我一把,以前的接口就是unsigned int的,但是地形坐标显然是有负值的,
然后.....,不知道xophiix的库里怎样处理的。
还有这个blogger,S键是publish,我按Ctrl+S按习惯了,又害死我了.....

2006年9月15日

struct内部成员对齐带来的问题

24位的bmp文件的头部信息是由如下的两个结构组成的。共54字节


struct SBitMapFileHeader
{
unsigned short bfType; // 位图文件的类型,必须为BM
unsigned long bfSize; // 位图文件的大小,以字节为单位
unsigned short bfReserved1; // 位图文件保留字,必须为0
unsigned short bfReserved2; // 位图文件保留字,必须为0
unsigned long bfOffBits; // 位图数据的起始位置,以相对于位图

};


struct SBitMapInforHeader
{
unsigned long biSize;
long biWidth;
long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned long biCompression;
unsigned long biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;

};

拿第一个struct做例子吧,用printf("%d",sizeof(SBitMapFileHeader));看一下大小是16字节。
三个unsigned short,两个unsigned long加起来是14字节,因为为了内存对齐,第一个unsigned short占了4个字节的内存区域。(实际用于存储的还是两个)。bmp里仍然是用14个字节来存储。所以fread(&mapHeader , 1 , sizeof(SBitMapFileHeader), pMapFile);的读取办法就会出错。(在VS.NET 2003和Dev C++是如此的)。
一种写法:

fread(tmp_head, 14, 1, fp_load); /* 此处用中转空间tmp_head */
file_header.bfType = tmp_head[0];
file_header.bfSize = (0xffff0000 & ((unsigned long)tmp_head[2] << 16) |
0x0000ffff & ((unsigned long)tmp_head[1]));
file_header.bfReserved1 = tmp_head[3];
file_header.bfReserved2 = tmp_head[4];
file_header.bfOffBits = (0xffff0000 & ((unsigned long)tmp_head[6] << 16) |
0x0000ffff & ((unsigned long)tmp_head[5]));

我看了觉得不爽,不够透明。写了这个:

fread(&mapHeader.bfType , 1 , sizeof(unsigned short) , pMapFile);
fread(&mapHeader.bfSize , 1 , sizeof(unsigned long ) , pMapFile);
fread(&mapHeader.bfReserved1 , 1 , sizeof(unsigned short) , pMapFile);
fread(&mapHeader.bfReserved2 , 1 , sizeof(unsigned short) , pMapFile);
fread(&mapHeader.bfOffBits , 1 , sizeof(unsigned long ) , pMapFile);

豆豆看了又觉得不爽,写了这个:

#pragma pack(1)
struct SBitMapFileHeader
{
unsigned short bfType; // 位图文件的类型,必须为BM
unsigned long bfSize; // 位图文件的大小,以字节为单位
unsigned short bfReserved1; // 位图文件保留字,必须为0
unsigned short bfReserved2; // 位图文件保留字,必须为0
unsigned long bfOffBits; // 位图数据的起始位置,以相对于位图

};
#pragma pack(4)

这样编译器就不会默认用4个字节对齐了,VS.NET 2003,W4(Warning 4)等级下有一个warning.不过已经是很完美的方案了。

2006年9月12日

NeHe的OpenGL教程(完全版)下载

NeHe的OpenGL教程(完全版)下载(chm)
但是不附带代码,代码可以去http://www.xophiix.com.cn下载,是打了包的,或者NeHe的网站
或者看电子书的时候看到那里,就下那一章的,书里有链接。NeHe的教程还是不错的,如果能从头到尾学下来,也有一定水平了。可惜最近不怎么更新了。

谷歌的宣传flash

谷歌的宣传flash,我觉得这个名字蛮好的吗。。

下载

教育网下的软件升级

很多软件的升级服务器用教育网是无法直接访问的。比如avast! antivirus,McAfee firewall,Dev C++,这三个是我升级成功的。说一下经验。(像norton这样大厂商的产品用教育网也可以升级)
avast !是绝好的杀毒软件,功能很强,也很为用户考虑,开机扫描(这个可是在没登陆的时候就进行的,跟开机磁盘检查用的同一个界面),检测内存,不在全屏模式(比如你正在玩游戏)弹出,可定制的自动防护操控,皮肤切换。。。而且免费两年。avast !的更新是可以设置代理的,我用的是221.208.173.84:3128。一直很稳定。
McAfee firewall,Dev C++没有代理设置,不过最近发现他们默认使用IE的代理,所以把IE代理设置好就可以了。Dev C++的升级并不指真正的升级而是check for updates.又很多好东东呢。Dev C++还是很有生命力的。NeHe也提供Dev C++源码的。OpenSSL,zlib,OGRE,boost,Python,glut等等。Dev C++也是有很多缺陷的这个很限制它,编辑器的诸多bug,工程编译的一些问题,CVS功能比较弱,调试不够强。check for updates只能从库的方面改进它,而不是从它本身,插件机制才是王道啊!

2006年9月5日

关于C语言的字符串的问题

这是最近一个同学问我的问题,大致如下


#include <stdio.h>
#include <string.>

void ShowFile(char *fileName);

int main()
{
ShowFile("test");
Printf("%s","Hello World");
}
void ShowFile(char *fileName)
{
FILE *pShowFile;
strcat(fileName,".txt");
fopen("fileName","r");
......
}

ShowFile函数的本意是打印一个文本文件的内容,但这个程序输出的时候,"Hello World"不见了,变成了”.txt"。
实际上常量字符串是作为静态(static)变量来存储的,位置在全局变量之后,strcat()语句在"test"这个字符串之后写上了信息,破坏了下一个字符串"Hello World"的结构。
改写前:t e s t '\0' H e l l o....
改写后:t e s t . t x t '\0'

这个问题通常是这样解决的

void ShowFile(char *fileName)
{
char tempFileName[MAX_FILE_NAME_LENGTH];
FILE *pShowFile;

strcpy(tempFileName, fileName);
strcat(tempFileName,".txt");
fopen("tempFileName","r");
......
}

C语言的字符串处理机制的却并不怎么好,而且不仅仅是静态字符串。想起《JOEL 说软件》提到的事情了。C语言中字符串是以‘\0'作为结束符的。获取字符串长度的办法就是:从串首开始遍历,直到遇到这个结束符。这个方案也许也并不怎样,效率很低。
Joel提到了Pascal的字符串处理机制,串的第一位存储长度,不仅获取长度的问题解决了,而且两个串合并的问题也变得更容易了。可以通过一次移位到字符串的末尾!

还讲了一个油漆匠的故事。他的名字我忘记了。就是说有一个油漆匠吧,他负责漆路面,第一天漆了很远,老板夸奖他。第二天没有前一天那么远了,但是也算不少。第三天,他非常的卖力,但是仅仅,漆了很短的距离。因为他离油漆桶越来越远了。这就是C语言的字符串,每一次操作都从头开始。要保留当前计算的结果,所以要带着油漆桶走。

2006年9月3日

Shadow Volume

弄了一天的 Shadow Volume,还是有点收获的。基本的过程是了解了。
单个三角形的情况还很容易理解,多个的还没想明白。要努力啊
整整弄了一天,也没有War3。快开学了,时间不多了,该做点事了
xophiix回来了,自己感觉还是很受鼓励的。
06.09.05
今天是非常的不爽,在代码即将写完的时候,VS.NET 2003顺利崩溃。更为致命的是产生了罕见的蓝屏错误,然后启动失败。然后启动了若干次启动到安全模式。check hard disk,这个硬盘一直有问题。哎,本来准备今天见到效果 的。
06.09.05
今天重写了,所有的物体都出来了,偏偏阴影不见了。3维程序调试起来也很麻烦,跟普通的不一样,他是看效果的。
06.09.06
终于改出一个能用的了。从NeHe的27课弄出来的。因为昨天写的那个是VS.NET 2003写的,用xophiix的框架,现在VS彻底崩了,无法编译了。NeHe的一个做法让我很不解,他从文件里读顶点信息,也就是说你要画一个物体,必须提前计算出所有顶点。实际上多个面可能有一个公共的顶点的。在OGL里就可以画每个面的时候都glVertex..... ,判断两个面是否相邻时判断顶点的坐标是否相等。我重载了struct SVertex的 == 操作符。尽管重载并不是好事。


bool operator== (SVertex p1, SVertex p2)
{
if(p1.x == p2.x && p1.y == p2.y && p1.z == p2.z )
return true;
else
return false;
}

还有矩阵那一部分也要改一下,其实没必要重新生成一个矩阵,用逆矩阵就可以了。实际上他那个矩阵变换我还是有点不明白的。
06.09.06
现在明白NeHe为什么那样读取数据了,很多模型文件(比如3ds)都是在前面存模型的顶点,面的信息里只有顶点的索引。听xophiix说的。
看来线性代数的东西还是比较重要,连个矩阵求逆都不会了。还到处找资料^_^
06.09.08
NeHe求逆矩阵的方法真是奇怪,按相反方向进行变换,然后glGetFloatv(GL_MODELVIEW_MATRIX, curMatrix);
其实有专门的矩阵函数的............,比如高斯-约旦法。不过我只有代码,具体的数学知识并不清楚。要开始研究3DS文件了。程序员们总喜欢做抽象层。。。
从NeHe的网站上看到一段话,会让很多人汗颜了
One final note, if you see code that you feel is similar to someone else's code, please contact me. I assure you, any code I borrow from or learn from either comes from the MSDN or from sites created to help teach people in a similar way that my site teaches GL. I never intentionally take code, and never would without giving the proper person credit. There may be instances where I get code from a free site not knowing that site took it from someone else, so if that happens, please contact me. I will either rewrite the code, or remove it from my program. Most the code should be original however, I only borrow when I absolutely have no idea how to accomplish something, and even then I make sure I understand the code before I decide to include it in my program. If you spot mistakes in any of the lessons, no matter how tiny the mistake may be, please let me know.
现在看来把
阴影加到3DS模型里还是比较容易的,3DS文件的定义和需要的数据差不多。明天得补一下功课了。可惜矩阵变换那一部分还是有点不明白,必须用我认为错误的方法才能得到正确的结果。。。
Z-PASS + 3DS基本上弄得差不多了,发张图先

要z-fail了,会是一个难点啊。还有今天因为要用Import3DS这个函数而从CLoad3DS继承了CBW3DSShadow被cactus和xophiix。。。。
的确是经验啊,继承不该被滥用。而且,有些成员为了这次继承变成了protected,又是滥用.......
06.09.14
今天例会又讲了一下Shadow Volume,才发现有些地方还是没真正弄明白,只是弄了一个能用的东西而已。像glLoadIdentity之后glRect产生的图形将覆盖整个屏幕。老是以为相机已经设定好了。还要改进啊,z-fail并不急,先用NV的改进方法吧。场景还要改善,新的地形和光束....
06.09.26
终于把ShadowVolume真正弄明白了,耶,还有soft shadow呢。4点多了。

2006年8月31日

学,然后知不足

来学校十天了,空空虚虚的一样
好像不知道要作什么,又有好多事要去做。很想去做图像识别,好好研究一下算法。又想把在图形学方面多做些努力,毕竟是理想啊。回想起来似乎一个暑假只做了一个简单的分形树,做来做去还是那个东西。
有时间的话可以好好看一下NeHe的教程,NeHe教程后面还是有些高级内容的。还有OpenGL红宝书也应该重看一下,对渲染管线根本没有个概念。C++也是.... ,C style,出了封装就没别的了。还有图形学,图形学还是比较重要的。这些书手上基本上都有,只是没有时间看一样,整天坐在电脑面前也好像没做什么。该努努力了。课设也要重视一下,做得深入一点。还有一本《Google 成功七堂课》,有时间也要好好看一下。
今天去NeHe的网站转了一圈,NeHe的贡献远不止几课教程那么简单,NeHe的下载页上有很多人,利用他的框架做出DEMO发出来,有些只是简单的更改,但是有些就非常cool了。做图形这东西,仅有技术还是不够的,要有创意,要懂点审美,art 。不过提供源代码的并不多,这个算是遗憾了,只从NeHe学到了一点技术,没学到NeHe的精神,共享精神。不管怎么说,又很多人开始爱好三维图形或者说Game Coding 了。
post:也许是有点贪玩,但是学东西还是需要一定的时间的,太小看这个时间了
xophiix终于回来了。要真的做点事了。还是很高兴的

2006年8月28日

C/C++中精确计时的两个方法(再加一个)

其实关于这个讨论已经很多了,去百度知道搜一下就看得见。最近写东西需要精确计量程序的运行时间,自己写了一个类,又改写了一个用部分汇编写的类。我写的那个大约在millisecond级,另一个估计起码microsecond级,不过在AMD处理器的机器上偶尔回测不准,他用了Intel 汇编指令。
点击下载源程序 (8k),(放在我的google pages 上的)
算了,不用去下载了,好麻烦的。

for(int testIndex = 0; testIndex &lt; testTimes; testIndex++)
{
//do something<br>
DoSomething();
}
timer -= clock();
printf("totol time:%ld,run one time cost:%f"
, timer
,((float)timer)/testTimes );

还有一种,既精确又通用的。精度还不大清楚

LARGE_INTEGER startTime;
LARGE_INTEGER endTime;

QueryPerformanceFrequency(&startTime);
//Do something but not evil
DoSomeThing();
QueryPerformanceFrequency(&endTime);

float elapsedTime = (float)endTime.QuadPart
- (float)startTime.QuadPart;

剩下的就不说了

简单的三维树生成及渲染


这个暑假写的学的东西,最近整理起来,刚刚学OpenGL,很多方面做得都不好。
压缩包里有介绍
DEMO下载
源文件下载

pre: 先反省一下,以前写的确实过于简略,代码又没有设么注释。感谢EmilMatthew的话,让更多人受益 源代码正在改写,以前变量命名很随意,反正很不清晰,更提不上elegant了。另一方面也在进行优化
核心算法描述:(实际上并不算是什么算法) 树主要分两部分:树枝和树干。树干使用二次曲面圆柱(gluCylinder,注意这个圆柱是可以上下半径不等的)近似,树叶就贴一片纹理。 树的绘制是递归进行的,也就是说:在一段树桩上接上三段树枝所形成的东西仍然叫做树(DEMO里每一段树枝上都有三段小树枝 ,递归的深度是四,可以自己调整)。用代码来写的话:


void DrawTree(int depth)
{
DrawStem(); //绘制树桩
if (2 < m_ndepthtop ="=">

//未绘制到顶层的话继续递归绘制
MoveToStemTop(); //移动到树桩顶部(注意圆柱体是没有上下表面的,所以要用树枝挡住)
DrawTree(depth + 1); //绘制第一个树枝

MoveToProperPos(); //移动到适当位置(glPushMatrix();glRotatef();glTranlatef();...)
DrawTree(depth + 1); //绘制第二个树枝
MoveToProperPos(); //移动到适当位置
DrawTree(depth + 1); //绘制第三个树枝
}


绘制函数可以这样被调用:DrawTree(1); m_nDepth指的是树的递归层数,加深一层速度会慢三倍,但是可以得到更繁茂的树。 DEMO里只有四层,只是用了很大的叶子去掩盖,并且一片纹理图上就有五片叶子。不过总比一棵树上贴五片树枝纹理图,然后用billboard 好的多,很多著名游戏都是这么干的。
post: 还有一点别的事情,树的顶端树枝很细,所以我并没有使用二次曲面进行模拟,而是使用了长方体。这样会稍微快一点。 比较粗的树干上通常是没有叶子的,所以绘制的过程中有相关的判断,地层的树枝上不会有叶子。 我用AMD CodeAnalyst 对程序进行了分析,可惜并没有找到真正的热点。对于代码优化来讲,算法上的优化通常会更优。 在我的机器(AMD Sempron 2800+ 1.6G,512MB,6600LE 128MB)上,把绘制树的过程做成显示列表后,调用一次列表的时间大约为0.85ms ,使用近似之后可以节约大约0.04ms。目前对我来说主要问题还是效率,我希望效率可以提升一倍,反正要比导入模型更快。 最近主要针对的是消除递归,树是递归定义的,希望消除递归能提高效率。主要方法还是用栈,二叉树和三叉树消除递归好像差不多。 还有隐藏面消除,显卡也会完成隐藏面消除,我希望要的结果是不需要绘制的面(或者点)的信息就不要传给显卡,估计自己做可能倒不如传给显卡。 还有更快的圆柱体绘制方法。 还有使用更小更精巧的纹理,看WAR3的纹理图给了我一点启发,一张256X256的图片巧妙的交错存储了两种植物的纹理。30多个面,50多个顶点 做出来的植物也是非常逼真,不过WAR3并不是以真实感为卖点的游戏,其实树做的就很搓,实时性可能更重要一些。 如果有人对这个感兴趣的话可以在我的blog上留言。
注:递归消除部分的代码已经写完,但是效率并没有得到提高

一个不错的WAR3 资源查看器

最近在国外的网站上发现的,因为正想把魔兽里的一个模型加到自己的一个东西里。以前也用过一些这类的东西,具体名字就不说了,但是这个功能是我所用过的最好的。对MPQ文件的分析能力很强。有一点新发现,原来蜘蛛用来攻击的是小蜘蛛。
还有那个草基本上只用了一块纹理,256 X 256的就拼出来一个很有立体感的东西。
War3ModelEditor 下载
它的网站上是有源码的:http://magos.thejefffiles.com/War3ModelEditor/
还有单独的导入MD2和MS3D的文件,找了好久的东西。

简单的素数表获取:埃拉托色尼筛


今天看到一个算法,集合的伪随机遍历。倒是还是不很明白,不过它需要获取素数表
,想起以前的一个算法:埃拉托色尼筛。
占用空间比较大,但是速度还过得去,基本上没用到乘法和除法。 还有就是不能检验单个素数。

比如说3是一个素数,6(3*2),9(3*3),12(3*4)......就都不是素数,所以prime[6],prime[9],prime[12]
就都被标记为0
当primeIndex增大到6的时候 ,因为prime[6]已经被标记所以 ,prime[12]不会被再次标记。

比如说要找从1 ~ 999之间的素数

/* prime.c
written by yaker 2006.08.07
*/

#include <stdio.h>
#include <math.h> /* sqrt(); */
#include <conio.h> /* getch(); useful for Dev C++ */

#define UPER_LIMIT 1000

int prime[UPER_LIMIT]; /* In fact in can be an array of bool in C++
,using pascal it even cost only 1000 bits */

int main()
{
int primeIndex,anotherIndex;

/* Initialize*/
for(primeIndex = 2;primeIndex < UPER_LIMIT;primeIndex++)
prime[primeIndex] = 1;

/* Check */
for(primeIndex = 2;primeIndex <= sqrt(UPER_LIMIT);primeIndex++)
{
if(prime[primeIndex])
{
anotherIndex = primeIndex * 2;
while(anotherIndex < UPER_LIMIT)
{
prime[anotherIndex] = 0;
anotherIndex += primeIndex;
}
}
}

/* OutPut */
for(primeIndex = 2;primeIndex < UPER_LIMIT;primeIndex++)
{
if(prime[primeIndex])
printf("%8d",primeIndex);
}

/* Pause */
getch();

return 0;
}




indent:代码整理工具

indent是C语言(不包括C++)的代码整理工具
这些日子在做课程设计,老师给的那个代码示例实在是过于dirty,所以找到了这个东东
indent有很多参数,所以可以依照个人习惯来定制,默认的是gnu风格,可选的风格还有K&R和Orign

下载

有一个叫MyIndent的工具,有翻译好的indent参数,而且可以预览。一点小小的不足是预览时Tab是八个空格,反正我常用四个,不大习惯。还有主界面不可以最大化。但是它仍然是很好的工具。
Indent的存在也许是一件很奇怪的事情,你需要它通常是你拿到了一段别人写的很乱的代码。如果自己注意代码风格的话是不需要它的。关于代码风格有非常多的文章,《代码大全》有一章的内容。
下载的包里含有CodeFormator.vb文件,可以把indent整合进VS.NET 2003。还需要别的文件都在MyIndent的包里,这个是作者忘记放进去的。我试着按作者的说明整合了,但是失败了。

MyIndent 能整合到 Visual Studio.NET 2003 中,通过宏代码调用 API 的方式格式化当前编辑的文件或选中的代码块,使用方法是打开宏 IDE(按快捷键Alt+F11),添加 CodeFormater.vb 文件到 MyMacro 中,修改其中的“I:\Richard\Programs\MyIndent\MyIndent.dll”和“I:\Richard\Programs\MyIndent\~out.tmp”两个字符串,替换成实际的路径并保存,关掉宏 IDE,打开宏资源管理器(按快捷键Alt+F8),运行 FormatFile 或 FormatSelection 宏便可。还可以为宏指定快捷键,使用起来更方便。
可以使用添加外部工具的方法,工具->外部工具 标题,命令:.....\indent.exe 参数:-bap -bbo -bl -bli0 -bs -c41 -cbi4 -cd41 -cp41 -di8 -fc1 -i4 -l100 -lc100 -lps -ncdw -nce -nlp -npcs -nprs -npsl -nsc -saf -sai -saw -sc -sob -ts4 -ut $(ItemPath) 路径:$(ItemDir)。VS.NET 2003 的宏约定与VC 6不同

还有一个更易用一点的astyle,可以为C,C++,Java整理,当然也丧失了更强的可定制性

IE7的问题

刚刚装了IE7 RC1(真是晚啊),IE7还是又很大改进的,多页面,广告拦截,RSS支持。。。
不过GMail无法打开了,解决办法就是https://mail.google.com
还有就是GMail老是被封,当然是开了GTalk。只是以前用Maxthon没怎么被封过。
只好将就一下了,还是很喜欢IE7的。^_^
不过因为什么我也不清楚。以前听说FireFox有这种问题,没听说IE也有 。
Blogger本身也出了点问题,总是重复登陆。跑到国外的论坛上看了一下,这个问题是比较普遍的,我觉得是cookies的问题,所以只好把blogger的所有cookies放行。也有传言是因为ZoneAlarm,我觉得可能不大,但是我用的也是ZA.