<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Multi-crash</title>
	<atom:link href="http://multi-crash.com/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://multi-crash.com</link>
	<description>多重崩溃——算法,游戏,3d开发</description>
	<lastBuildDate>Sat, 24 Jul 2010 07:36:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>SlimXml和TinyXml，RapidXml的性能对比</title>
		<link>http://multi-crash.com/?p=278</link>
		<comments>http://multi-crash.com/?p=278#comments</comments>
		<pubDate>Sat, 17 Jul 2010 16:14:11 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[程序/算法]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://multi-crash.com/?p=278</guid>
		<description><![CDATA[前两天有朋友问，我的SlimXml有没有和RapidXml对比过效率？我是第一次听说这个库，更不用说对比效率了，于是上他们网站看了下。 好家伙，居然号称比TinyXml快30～60倍，而且是Boost.PropertyTree的默认xml解析器。 于是有点好奇，因为以前也没有特别关心过SlimXml的效率。 于是分别下载了TinyXml-2.6.1和RapidXml-1.13，迅速用vc8建立了两个测试工程，在系统中搜&#8221;*.xml&#8221;，找到了一个比较合适的测试文件。它足够大（1.5M），utf-8编码并且包含中/英文，有一定层次深度，大约3.3万行。测试文件可以从这里下载 测试对象是三个库从内存字符串解析xml的函数，这样能排除从硬盘上读文件这种不稳定因素的干扰，而且RapidXml貌似只支持从内存里解析 slim::XmlDocument::loadFromMemory() TiXmlDocument::Parse() rapidxml::xml_document&#60;char&#62;::parse&#60;flag&#62;() 要说明的是，RapidXml的这个parse是一个模板函数，必须给一个flag的参数，我测试的时候给的是默认的0 测试结果，解析这个3.3万行，1.5M大小的xml，三个库分别花了 SlimXml:    22ms TinyXml:    54ms RapidXml: 4ms! 结论是，RapidXml果然很强悍，居然比我的SlimXml快5倍多。但是并没有如作者所说比TinyXml快30~60倍，只有不到15倍。据说对比用的是一个约50k大小的xml文件，可惜并没有提供下载，不然可以验证一下。 比较欣慰的是，在我并没有很关注效率的情况下，SlimXml仍然比TinyXml快2.5倍。SlimXml走的是简单小巧路线，源代码只有32k，而TinyXml和RapidXml的源码分别是147k和141k，有这样的效率可以满意了。在我有很多空闲以前，估计我也不会再去优化它，因为这个库主要还是针对几十上百行的小文件，解析特别大的xml不在我考虑的范围之内。 以下是RapidXml提供的常见xml库效率对照表，其中还很牛鼻地提供了和strlen()函数的效率对比 我估计RapidXml速度快的主要原因是对memory pool的使用，毕竟在解析过程中需要创建大量的string，可以想象用memory pool和直接走默认的new很容易产生超过一个数量级的效率差异。]]></description>
			<content:encoded><![CDATA[<p>前两天有朋友问，我的<a href="http://multi-crash.com/?page_id=161" target="_blank"><span style="color: #0000ff;">SlimXml</span></a>有没有和<a href="http://rapidxml.sourceforge.net/" target="_blank"><span style="color: #0000ff;">RapidXml</span></a>对比过效率？我是第一次听说这个库，更不用说对比效率了，于是上他们网站看了下。</p>
<p>好家伙，居然号称比<a href="http://sourceforge.net/projects/tinyxml/" target="_blank"><span style="color: #0000ff;">TinyXml</span></a>快30～60倍，而且是<a href="http://boost.org/"><span style="color: #0000ff;">Boost.PropertyTree</span></a>的默认xml解析器。</p>
<p>于是有点好奇，因为以前也没有特别关心过SlimXml的效率。</p>
<p>于是分别下载了TinyXml-2.6.1和RapidXml-1.13，迅速用vc8建立了两个测试工程，在系统中搜&#8221;*.xml&#8221;，找到了一个比较合适的测试文件。它足够大（1.5M），utf-8编码并且包含中/英文，有一定层次深度，大约3.3万行。测试文件可以<a href="http://multi-crash.com/download/test.zip" target="_blank"><span style="color: #0000ff;">从这里下载</span></a></p>
<p>测试对象是三个库从内存字符串解析xml的函数，这样能排除从硬盘上读文件这种不稳定因素的干扰，而且RapidXml貌似只支持从内存里解析</p>
<ul>
<li>slim::XmlDocument::loadFromMemory()</li>
<li>TiXmlDocument::Parse()</li>
<li>rapidxml::xml_document&lt;char&gt;::parse&lt;flag&gt;()</li>
</ul>
<p>要说明的是，RapidXml的这个parse是一个模板函数，必须给一个flag的参数，我测试的时候给的是默认的0</p>
<p>测试结果，解析这个3.3万行，1.5M大小的xml，三个库分别花了</p>
<ul>
<li>SlimXml:    22ms</li>
<li>TinyXml:    54ms</li>
<li>RapidXml: 4ms!</li>
</ul>
<p><span id="more-278"></span>结论是，RapidXml果然很强悍，居然比我的SlimXml快5倍多。但是并没有如作者所说比TinyXml快30~60倍，只有不到15倍。据说对比用的是一个约50k大小的xml文件，可惜并没有提供下载，不然可以验证一下。</p>
<p>比较欣慰的是，在我并没有很关注效率的情况下，SlimXml仍然比TinyXml快2.5倍。SlimXml走的是简单小巧路线，源代码只有32k，而TinyXml和RapidXml的源码分别是147k和141k，有这样的效率可以满意了。在我有很多空闲以前，估计我也不会再去优化它，因为这个库主要还是针对几十上百行的小文件，解析特别大的xml不在我考虑的范围之内。</p>
<p>以下是RapidXml提供的常见xml库效率对照表，其中还很牛鼻地提供了和strlen()函数的效率对比</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/07/xml.png"><img class="alignnone size-full wp-image-279" title="xml" src="http://multi-crash.com/blog/wp-content/uploads/2010/07/xml.png" alt="" width="582" height="275" /></a></p>
<p>我估计RapidXml速度快的主要原因是对memory pool的使用，毕竟在解析过程中需要创建大量的string，可以想象用memory pool和直接走默认的new很容易产生超过一个数量级的效率差异。</p>
]]></content:encoded>
			<wfw:commentRss>http://multi-crash.com/?feed=rss2&amp;p=278</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Grandpa Demo on iPhone</title>
		<link>http://multi-crash.com/?p=275</link>
		<comments>http://multi-crash.com/?p=275#comments</comments>
		<pubDate>Mon, 05 Jul 2010 16:26:22 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[程序/算法]]></category>
		<category><![CDATA[3d]]></category>
		<category><![CDATA[骨骼动画]]></category>
		<category><![CDATA[Grandpa]]></category>

		<guid isPermaLink="false">http://multi-crash.com/?p=275</guid>
		<description><![CDATA[Grandpa从设计之初就定位于跨平台，渲染API无关的库。核心代码仅依赖c++标准库，理论上可以在任何平台，用任何（基本）符合c++标准的编译器编译。 但是这次还是碰到了一些问题，主要还是VC用的时间太长了，没有意识到某些特性实际上是非标准的，例如： hash_map头文件的位置不同。大家都知道该容器是非标准的，在vc下该头文件和其他容器在一起，而gcc下在ext子目录中 gcc警告最后一行非空（也就是说源代码文件最后一个字符必须是回车，标准的确是这么规定的），vc不会警告 vc允许只声明enum，gcc不允许 gcc没有“安全版”（带_s）的字符串处理函数 另外，Mac OS X（以及Linux等其他Unix系的操作系统）是用utf-8表示unicode，而Windows则采用utf-16，这一点也引起了一些小麻烦。 还有一个很讨厌，gcc居然不认识utf-8的bom(ef,bb,bf)。导致我所有的源代码文件都要改为multibytes编码 好在这些都是小问题，花了不到一天时间就都解决了。 特别感谢张志鹏同学，帮我把原来基于d3d的Demo移植到了Opengl es上。 另外从今天开始，Grandpa SDK开放svn更新，地址为 Http://www.multi-crash.com/release 推荐使用TortoiseSVN。]]></description>
			<content:encoded><![CDATA[<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/07/iphone.png"><img class="alignnone size-full wp-image-276" title="iphone" src="http://multi-crash.com/blog/wp-content/uploads/2010/07/iphone.png" alt="" width="379" height="664" /></a></p>
<p>Grandpa从设计之初就定位于跨平台，渲染API无关的库。核心代码仅依赖c++标准库，理论上可以在任何平台，用任何（基本）符合c++标准的编译器编译。</p>
<p>但是这次还是碰到了一些问题，主要还是VC用的时间太长了，没有意识到某些特性实际上是非标准的，例如：</p>
<div id="_mcePaste">
<ul>
<li>hash_map头文件的位置不同。大家都知道该容器是非标准的，在vc下该头文件和其他容器在一起，而gcc下在ext子目录中</li>
<li>gcc警告最后一行非空（也就是说源代码文件最后一个字符必须是回车，标准的确是这么规定的），vc不会警告</li>
<li>vc允许只声明enum，gcc不允许</li>
<li>gcc没有“安全版”（带_s）的字符串处理函数</li>
</ul>
</div>
<p>另外，Mac OS X（以及Linux等其他Unix系的操作系统）是用utf-8表示unicode，而Windows则采用utf-16，这一点也引起了一些小麻烦。</p>
<p>还有一个很讨厌，gcc居然不认识utf-8的bom(ef,bb,bf)。导致我所有的源代码文件都要改为multibytes编码</p>
<p>好在这些都是小问题，花了不到一天时间就都解决了。</p>
<p>特别感谢张志鹏同学，帮我把原来基于d3d的Demo移植到了Opengl es上。</p>
<p>另外从今天开始，Grandpa SDK开放svn更新，地址为 <a href="http://www.multi-crash.com/release" target="_blank"><span style="color: #0000ff;">Http://www.multi-crash.com/release</span></a></p>
<p>推荐使用<a title="sdf" href="http://tortoisesvn.net/downloads" target="_blank"><span style="color: #0000ff;">TortoiseSVN</span></a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://multi-crash.com/?feed=rss2&amp;p=275</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>骨骼动画的数据压缩（二）</title>
		<link>http://multi-crash.com/?p=271</link>
		<comments>http://multi-crash.com/?p=271#comments</comments>
		<pubDate>Sat, 12 Jun 2010 17:26:46 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[程序/算法]]></category>
		<category><![CDATA[3d]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[骨骼动画]]></category>

		<guid isPermaLink="false">http://multi-crash.com/?p=271</guid>
		<description><![CDATA[上一篇遗留了一个问题没有说完，就是关于绝对变换和相对变换。我们的骨架一般是以树形结构存储的，一个骨骼会有一个父骨骼（根骨骼除外）和若干个子骨骼。所以对于骨骼的变换我们可以存储在模型空间中的绝对变换，也可以存储相对于其父骨骼的相对变换，二者是可以相互转换的。 用相对变换形式存储的好处主要是便于拆分成不同类型的变换从而使动画插值成为可能；而保存绝对变换的好处首先是效率较高，因为在蒙皮计算的时候我们真正需要的是骨骼的绝对变换，如果保存的是相对变换就需要根据骨骼的父子关系先计算出绝对变换矩阵，每块骨骼都多出一个矩阵乘法的开销。另外直接保存绝对变换还能避免因为逐层相对变换引起的累积误差。 言归正传，我们继续来看动画数据的压缩，前文已经把200k的数据压缩到60k左右，还有办法继续压缩吗？ 用过Max（Maya和XSI不太熟悉，但想来也应该差不多，下文只讨论Max）的同学会发现，虽然我们的动画定的是30fps的标准，但是美术实际摆放的关键帧要远远低于这个密度，对于简单的动画，某些骨骼可能在两秒的时间内只有3～4个关键，其它时间的骨骼方位实际上是导出时Max自动插值得到的。于是小算盘就开始打了，既然我们保存的骨骼关键帧大部分都是Max自动生成的，那么我们能不能只保存真正由美术摆放的关键帧，剩下的也象Max所做的那样自动插值出来呢？如果可以的话，无疑将再次大幅减小动画文件的尺寸。 这个想法很诱人，而且答案基本是肯定的，但是要解决几个问题： 首先，Max的帧间插值方式不是线性的，而我们通常运行时采用的都是线性插值的方法，如果仅导出“美术关键帧”，在运行时就得不到在Max里看到的同样的效果 这个问题不难解决，只要实现一个和Max一样的非线性插值算法就行了，这一点Grandpa已经做到。当然，非线性插值一定会比线性的慢一些，如果效率比较吃紧的话，可以在动画文件加载完先按30fps的帧率非线性插值出均匀的关键帧（就像从Max里导出时所做的）放在内存里，在播放动画时仍然用线性插值；如果效率不是特别敏感（以我的经验动画帧间插值的开销占整体的比例非常小），可以直接在播放动画时采用非线性采样，这样不但节省了硬盘空间，连内存都省了。 另外一个比较麻烦的问题是，美术制作动画时几乎不可避免会用到IK控制器，例如走路时贴地的脚，持双手武器（比如长矛）时的“从动手”，甚至头部和视线的移动等等。IK控制是不能体现在关键帧里的，所以如果想达到只导出“美术关键帧”的目的，你的插件必须能同时导出这些IK控制的参数，而且引擎也必须能够实时处理。 待续……]]></description>
			<content:encoded><![CDATA[<p>上一篇遗留了一个问题没有说完，就是关于绝对变换和相对变换。我们的骨架一般是以树形结构存储的，一个骨骼会有一个父骨骼（根骨骼除外）和若干个子骨骼。所以对于骨骼的变换我们可以存储在模型空间中的绝对变换，也可以存储相对于其父骨骼的相对变换，二者是可以相互转换的。</p>
<p>用相对变换形式存储的好处主要是便于拆分成不同类型的变换从而使动画插值成为可能；而保存绝对变换的好处首先是效率较高，因为在蒙皮计算的时候我们真正需要的是骨骼的绝对变换，如果保存的是相对变换就需要根据骨骼的父子关系先计算出绝对变换矩阵，每块骨骼都多出一个矩阵乘法的开销。另外直接保存绝对变换还能避免因为逐层相对变换引起的累积误差。</p>
<p>言归正传，我们继续来看动画数据的压缩，前文已经把200k的数据压缩到60k左右，还有办法继续压缩吗？</p>
<p>用过Max（Maya和XSI不太熟悉，但想来也应该差不多，下文只讨论Max）的同学会发现，虽然我们的动画定的是30fps的标准，但是美术实际摆放的关键帧要远远低于这个密度，对于简单的动画，某些骨骼可能在两秒的时间内只有3～4个关键，其它时间的骨骼方位实际上是导出时Max自动插值得到的。于是小算盘就开始打了，既然我们保存的骨骼关键帧大部分都是Max自动生成的，那么我们能不能只保存真正由美术摆放的关键帧，剩下的也象Max所做的那样自动插值出来呢？如果可以的话，无疑将再次大幅减小动画文件的尺寸。</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/06/bar.jpg"><img class="alignnone size-full wp-image-272" title="bar" src="http://multi-crash.com/blog/wp-content/uploads/2010/06/bar.jpg" alt="" width="1009" height="110" /></a></p>
<p><span id="more-271"></span>这个想法很诱人，而且答案基本是肯定的，但是要解决几个问题：</p>
<p>首先，Max的帧间插值方式不是线性的，而我们通常运行时采用的都是线性插值的方法，如果仅导出“美术关键帧”，在运行时就得不到在Max里看到的同样的效果</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/06/spline.jpg"><img class="alignnone size-full wp-image-273" title="spline" src="http://multi-crash.com/blog/wp-content/uploads/2010/06/spline.jpg" alt="" width="496" height="363" /></a></p>
<p>这个问题不难解决，只要实现一个和Max一样的非线性插值算法就行了，这一点<a title="sdf" href="http://multi-crash.com/?page_id=158" target="_blank">Grandpa</a>已经做到。当然，非线性插值一定会比线性的慢一些，如果效率比较吃紧的话，可以在动画文件加载完先按30fps的帧率非线性插值出均匀的关键帧（就像从Max里导出时所做的）放在内存里，在播放动画时仍然用线性插值；如果效率不是特别敏感（以我的经验动画帧间插值的开销占整体的比例非常小），可以直接在播放动画时采用非线性采样，这样不但节省了硬盘空间，连内存都省了。</p>
<p>另外一个比较麻烦的问题是，美术制作动画时几乎不可避免会用到IK控制器，例如走路时贴地的脚，持双手武器（比如长矛）时的“从动手”，甚至头部和视线的移动等等。IK控制是不能体现在关键帧里的，所以如果想达到只导出“美术关键帧”的目的，你的插件必须能同时导出这些IK控制的参数，而且引擎也必须能够实时处理。</p>
<p>待续……</p>
]]></content:encoded>
			<wfw:commentRss>http://multi-crash.com/?feed=rss2&amp;p=271</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>骨骼动画的数据压缩（一）</title>
		<link>http://multi-crash.com/?p=270</link>
		<comments>http://multi-crash.com/?p=270#comments</comments>
		<pubDate>Fri, 11 Jun 2010 16:44:05 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[程序/算法]]></category>
		<category><![CDATA[3d]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[骨骼动画]]></category>

		<guid isPermaLink="false">http://multi-crash.com/?p=270</guid>
		<description><![CDATA[这里说的骨骼动画数据，是指美术用Max，Maya或者XSI之类的3d工具制作的角色动画，通常要导出成引擎可以读取的特殊格式，为游戏所用。对于目前越来越强调动作感的网络游戏来说，动画资源占整个安装包尺寸的比例在不断上升，动画数据的压缩也在变得原来越重要，更不用说采用大量实时渲染过场动画的次世代Console游戏了。 每个引擎都有自己独特的动画文件格式，但里面的内容都大同小异。所谓动画，就是一系列关键帧的集合，每个关键帧可以描述了角色整个骨架的完整姿态信息，也就是每一块骨骼在3d空间中的位置和方向。 假设我们有一个包含50块骨骼的骨架，采用30fps的帧率，一个长度为两秒的动画需要占用多大的空间呢？我们来计算一下。我们知道空间的方位可以用一个矩阵来描述，一个矩阵包含4 x 4 = 16个浮点数，也就是64字节；我们有50块骨骼，那么每个关键帧就需要50 x 64 = 3200字节，另外关键帧还需要一个4字节的时间值，那么就是3204字节；两秒的30fps动画一共包含60个关键帧，于是可以算出最终的大小是3204 x 60 = 192240字节，不到200k的样子。这就是未经压缩的数据大小，当然这里忽略了一些诸如骨骼名字之类的信息，和我们要讨论的动画压缩关系不大，就不算在内了。 有哪些手段可以压缩这些数据呢？我们可以从多个方面入手。 首先，描述一个空间方位真的需要64字节那么多数据吗？一般来说，我们的动画不会包含象“切变”这样的变换，我们只需要旋转，平移和缩放就足够了，所以我们可以用两个表示平移和缩放的vector3（3个浮点数），以及一个表示旋转的四元数（quaternion，4个浮点数）就可以描述一个骨骼方位，（ 3 + 3 ＋4 ）x sizeof(float) = 40，每块骨骼一下省掉了24个字节。从矩阵中抽取平移，旋转和缩放信息的函数每个3d数学库都会有，不在这里讨论。 其次我们会发现，某些骨骼在整个动画过程中都没有动过（这里要引入一个绝对变换和相对变换的概念，下文详述），这样等于在每个关键帧里描述这些骨骼方位的数据都是一样的，无疑是一个很大的浪费。为了解决这个问题，我们可以修改一下数据结构，让每个骨骼分别保存自己的关键帧数据，这样一来不同骨骼的关键帧数量可以不同，对于那些始终保持不动的骨骼，可以只保存一个关键帧，这样就省多了。但是要注意，骨骼数据分开以后每个骨骼的关键帧都要记录自己的时间，所以这方面的空间占用反而会增加，但通常来说总体上是划算的。 顺着这个思路，对于同一块骨骼，可能某一类变换（比如缩放）在整个动画过程中都不会有变化，于是我们再改进一下数据结构，把每块骨骼的不同类型变换的关键帧也分开存储，也就是分成平移关键帧，旋转关键帧和缩放关键帧。这样一来我们就会欣喜地发现，对于角色动画，大部分骨骼都只有旋转的动画，一般只有根骨骼会有平移动画，缩放一般都不会用到。其实这从常识角度很容易理解，一般的生物（除了软体动物）骨骼都只能旋转，哪有能随便改变长度（平移）的，缩放就更不用说了。 现在比较理想的情况是，每块骨骼只需要 1个平移关键帧：12字节vector3加4字节time 1个缩放关键帧：12字节vector3加4字节time（如果整个动画都没有用到缩放，可以把这个都省掉，所有骨骼的缩放都默认为单位向量） 60个旋转关键帧：16字节quaternion加4字节time 一共是16 + 16 + 20 * 60 =  1232字节，一共50块骨骼就是61600字节，不到原始数据（近200k）的1/3了！ 睡觉，待续。]]></description>
			<content:encoded><![CDATA[<p>这里说的骨骼动画数据，是指美术用Max，Maya或者XSI之类的3d工具制作的角色动画，通常要导出成引擎可以读取的特殊格式，为游戏所用。对于目前越来越强调动作感的网络游戏来说，动画资源占整个安装包尺寸的比例在不断上升，动画数据的压缩也在变得原来越重要，更不用说采用大量实时渲染过场动画的次世代Console游戏了。</p>
<p>每个引擎都有自己独特的动画文件格式，但里面的内容都大同小异。所谓动画，就是一系列关键帧的集合，每个关键帧可以描述了角色整个骨架的完整姿态信息，也就是每一块骨骼在3d空间中的位置和方向。</p>
<p>假设我们有一个包含50块骨骼的骨架，采用30fps的帧率，一个长度为两秒的动画需要占用多大的空间呢？我们来计算一下。我们知道空间的方位可以用一个矩阵来描述，一个矩阵包含4 x 4 = 16个浮点数，也就是64字节；我们有50块骨骼，那么每个关键帧就需要50 x 64 = 3200字节，另外关键帧还需要一个4字节的时间值，那么就是3204字节；两秒的30fps动画一共包含60个关键帧，于是可以算出最终的大小是3204 x 60 = 192240字节，不到200k的样子。这就是未经压缩的数据大小，当然这里忽略了一些诸如骨骼名字之类的信息，和我们要讨论的动画压缩关系不大，就不算在内了。</p>
<p><span id="more-270"></span></p>
<p>有哪些手段可以压缩这些数据呢？我们可以从多个方面入手。</p>
<p>首先，描述一个空间方位真的需要64字节那么多数据吗？一般来说，我们的动画不会包含象“切变”这样的变换，我们只需要旋转，平移和缩放就足够了，所以我们可以用两个表示平移和缩放的vector3（3个浮点数），以及一个表示旋转的四元数（quaternion，4个浮点数）就可以描述一个骨骼方位，（ 3 + 3 ＋4 ）x sizeof(float) = 40，每块骨骼一下省掉了24个字节。从矩阵中抽取平移，旋转和缩放信息的函数每个3d数学库都会有，不在这里讨论。</p>
<p>其次我们会发现，某些骨骼在整个动画过程中都没有动过（这里要引入一个绝对变换和相对变换的概念，下文详述），这样等于在每个关键帧里描述这些骨骼方位的数据都是一样的，无疑是一个很大的浪费。为了解决这个问题，我们可以修改一下数据结构，让每个骨骼分别保存自己的关键帧数据，这样一来不同骨骼的关键帧数量可以不同，对于那些始终保持不动的骨骼，可以只保存一个关键帧，这样就省多了。但是要注意，骨骼数据分开以后每个骨骼的关键帧都要记录自己的时间，所以这方面的空间占用反而会增加，但通常来说总体上是划算的。</p>
<p>顺着这个思路，对于同一块骨骼，可能某一类变换（比如缩放）在整个动画过程中都不会有变化，于是我们再改进一下数据结构，把每块骨骼的不同类型变换的关键帧也分开存储，也就是分成平移关键帧，旋转关键帧和缩放关键帧。这样一来我们就会欣喜地发现，对于角色动画，大部分骨骼都只有旋转的动画，一般只有根骨骼会有平移动画，缩放一般都不会用到。其实这从常识角度很容易理解，一般的生物（除了软体动物）骨骼都只能旋转，哪有能随便改变长度（平移）的，缩放就更不用说了。</p>
<p>现在比较理想的情况是，每块骨骼只需要</p>
<ul>
<li>1个平移关键帧：12字节vector3加4字节time</li>
<li>1个缩放关键帧：12字节vector3加4字节time（如果整个动画都没有用到缩放，可以把这个都省掉，所有骨骼的缩放都默认为单位向量）</li>
<li>60个旋转关键帧：16字节quaternion加4字节time</li>
</ul>
<p>一共是16 + 16 + 20 * 60 =  1232字节，一共50块骨骼就是61600字节，不到原始数据（近200k）的1/3了！</p>
<p>睡觉，待续。</p>
]]></content:encoded>
			<wfw:commentRss>http://multi-crash.com/?feed=rss2&amp;p=270</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>D3D9的Pixel和Texel</title>
		<link>http://multi-crash.com/?p=256</link>
		<comments>http://multi-crash.com/?p=256#comments</comments>
		<pubDate>Wed, 19 May 2010 07:10:01 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[程序/算法]]></category>
		<category><![CDATA[3d]]></category>

		<guid isPermaLink="false">http://multi-crash.com/?p=256</guid>
		<description><![CDATA[名词解释：Pixel&#8211;屏幕像素；Texel&#8211;纹理像素 在使用d3d来渲染GUI元素时，可能很多人都碰到过一些让人抓狂的灵异问题，例如整个界面变模糊，边缘偏差一个像素，甚至在不同显卡上的表现还不一样…… 这一般都是Pixel和Texel的对应出了问题导致的，界面元素的渲染的主要特点是一般是要求屏幕像素和纹理像素严格一一对应，这个在2d绘图中看来很自然的事，到了3d环境下似乎变得不那么简单。 d3d的特点是确定投影范围(记住渲染UI要使用正交投影)以后，“整数位置坐标”对应的是屏幕像素中心，而“整数纹理坐标”对应的却是纹理像素的边缘，这就是一切罪恶的根源。怎么理解这句话呢？请继续往下看…… 一般来说渲染GUI时我们会把视口大小设置为和背缓分辨率一致，这样3d坐标的单位“1”就和屏幕上1个像素对应；而纹理坐标则用待渲染图元的像素尺寸占整张纹理像素尺寸的比例来确定。这里面有什么问题呢？ 我们举个极端的例子，假设我们要在坐标0,0点附近用一张4&#215;4的纹理渲染一个4&#215;4像素的正方形，我们用两个三角形来渲染，4个顶点坐标（这里忽略z值）分别为(0,0),(4,0),(0,4),(4,4)；因为图元使用了整张纹理，所以三角形的uv坐标为左上角(0,0)，右下角(1,1)。 上中黑色的X,Y轴为投影后的逻辑坐标轴，可以看到它们是穿过像素（灰色方格代表像素）中心的。另外红色方格代表的是纹理坐标的像素。 现在大家可以比较直观的看到，屏幕像素和纹理像素是不重合的。那实际绘制的结果会是什么样的呢？我们知道，显卡在光栅化的时候决定一个像素是否渲染是看该像素中心是否位于三角形内。再看上图我们会发现，三角形边缘刚好穿过了某些像素的中心，那到底这些像素是算在三角形内还是三角形外呢？很遗憾，由于浮点精度，显卡驱动等原因，结果可能是不确定的，也就是说你最终可能得到的是下面两个之一(运气好的话) 或者甚至是下面两个之一 同时，采样纹理时也是按像素中心点的uv坐标来采样的，图中可以看出要采样uv位置也都落在纹理像素的边缘上！如果用的是点采样(D3DTEXF_POINT)的话这里又是另一个不确定因素。 那这个问题怎么解决呢？说出来很简单(虽然感觉有点土)，就是把三角形的顶点位置偏移半个像素(下图左)，或者将摄像机位置偏移半个像素(这样投影坐标轴就落在像素边缘，而不是中心了，下图右)也可以，总之就是让Pixel和Texel重合，三角形的边(除了“斜边”)都落在像素边缘，Pixel的uv也都落在Texel中心点，于是天下就太平了…… 以上讨论的仅限于我相对熟悉的D3d9，据说在OpenGL里不是这样，而且我印象中D3d10也有所改变。]]></description>
			<content:encoded><![CDATA[<p>名词解释：Pixel&#8211;屏幕像素；Texel&#8211;纹理像素</p>
<p>在使用d3d来渲染GUI元素时，可能很多人都碰到过一些让人抓狂的灵异问题，例如整个界面变模糊，边缘偏差一个像素，甚至在不同显卡上的表现还不一样……</p>
<p>这一般都是Pixel和Texel的对应出了问题导致的，界面元素的渲染的主要特点是一般是要求屏幕像素和纹理像素严格一一对应，这个在2d绘图中看来很自然的事，到了3d环境下似乎变得不那么简单。</p>
<p>d3d的特点是确定投影范围(记住渲染UI要使用正交投影)以后，<span style="color: #ff0000;">“整数位置坐标”对应的是屏幕像素中心，而“整数纹理坐标”对应的却是纹理像素的边缘</span>，这就是一切罪恶的根源。怎么理解这句话呢？请继续往下看……</p>
<p>一般来说渲染GUI时我们会把视口大小设置为和背缓分辨率一致，这样3d坐标的单位“1”就和屏幕上1个像素对应；而纹理坐标则用待渲染图元的像素尺寸占整张纹理像素尺寸的比例来确定。这里面有什么问题呢？</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/05/texcel11.png"><img class="alignnone size-full wp-image-258" title="texcel1" src="http://multi-crash.com/blog/wp-content/uploads/2010/05/texcel11.png" alt="" width="399" height="283" /></a></p>
<p><span id="more-256"></span>我们举个极端的例子，假设我们要在坐标0,0点附近用一张4&#215;4的纹理渲染一个4&#215;4像素的正方形，我们用两个三角形来渲染，4个顶点坐标（这里忽略z值）分别为(0,0),(4,0),(0,4),(4,4)；因为图元使用了整张纹理，所以三角形的uv坐标为左上角(0,0)，右下角(1,1)。</p>
<p>上中黑色的X,Y轴为投影后的逻辑坐标轴，可以看到它们是穿过像素（灰色方格代表像素）中心的。另外红色方格代表的是纹理坐标的像素。</p>
<p>现在大家可以比较直观的看到，屏幕像素和纹理像素是不重合的。那实际绘制的结果会是什么样的呢？我们知道，显卡在光栅化的时候<span style="color: #ff0000;">决定一个像素是否渲染是看该像素中心是否位于三角形内</span>。再看上图我们会发现，三角形边缘刚好穿过了某些像素的中心，那到底这些像素是算在三角形内还是三角形外呢？很遗憾，由于浮点精度，显卡驱动等原因，结果可能是不确定的，也就是说你最终可能得到的是下面两个之一(运气好的话)</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/05/texcel21.png"><img class="alignnone size-full wp-image-264" title="texcel2" src="http://multi-crash.com/blog/wp-content/uploads/2010/05/texcel21.png" alt="" width="664" height="283" /></a></p>
<p>或者甚至是下面两个之一</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/05/texcel41.png"><img class="alignnone size-full wp-image-266" title="texcel4" src="http://multi-crash.com/blog/wp-content/uploads/2010/05/texcel41.png" alt="" width="664" height="283" /></a></p>
<p>同时，采样纹理时也是按像素中心点的uv坐标来采样的，图中可以看出要采样uv位置也都落在纹理像素的边缘上！如果用的是点采样(D3DTEXF_POINT)的话这里又是另一个不确定因素。</p>
<p>那这个问题怎么解决呢？说出来很简单(虽然感觉有点土)，就是把三角形的顶点位置偏移半个像素(下图左)，或者将摄像机位置偏移半个像素(这样投影坐标轴就落在像素边缘，而不是中心了，下图右)也可以，总之就是让Pixel和Texel重合，三角形的边(除了“斜边”)都落在像素边缘，Pixel的uv也都落在Texel中心点，于是天下就太平了……</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/05/texcel61.png"><img class="alignnone size-full wp-image-267" title="texcel6" src="http://multi-crash.com/blog/wp-content/uploads/2010/05/texcel61.png" alt="" width="656" height="293" /></a></p>
<p>以上讨论的仅限于我相对熟悉的D3d9，据说在OpenGL里不是这样，而且我印象中D3d10也有所改变。</p>
]]></content:encoded>
			<wfw:commentRss>http://multi-crash.com/?feed=rss2&amp;p=256</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Visual Studio 2010试用印象</title>
		<link>http://multi-crash.com/?p=249</link>
		<comments>http://multi-crash.com/?p=249#comments</comments>
		<pubDate>Sun, 09 May 2010 15:10:20 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[程序/算法]]></category>

		<guid isPermaLink="false">http://multi-crash.com/?p=249</guid>
		<description><![CDATA[VS 2010发布也有一阵了，五一的时候下载了一个30天Trial试了一下。当然，我只用其中的Visual c++那部分。 不出所料，界面上看变化不大，就是默认的配色更酷了一些 颇值得一提的是Edit菜单里多了一个Navigate To&#8230;，能快速索引到Symbol或者文件名，对于它我只想说，finally！终于可以安心告别Visual Assist了 据说这次的c++编译器被重写了很大一部分，对于新支持的一堆新特性我暂时还不太关心，更重要的是编译出来目标代码的质量。把Grandpa工程转到2010下重编了下，结果得到的Grandpa.dll是215K，而之前vs2005编译出来的是252K。运行了一下Demo，得到的性能评估数据表明，测试模型的更新时间从约0.16ms缩短到了约0.14ms。目标代码的大小和运行速度都得到了10%以上的改善，结果令人满意。当然，我只是试了一个工程，不具有普遍参考意义。 希望vc 2010真的能象vc6一样成为下一个经典，另外如果能让我们能买得起就更好了（至少是Professional版）。]]></description>
			<content:encoded><![CDATA[<p>VS 2010发布也有一阵了，五一的时候下载了一个30天Trial试了一下。当然，我只用其中的Visual c++那部分。</p>
<p>不出所料，界面上看变化不大，就是默认的配色更酷了一些</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/05/vs2010.jpg"><img class="alignnone size-full wp-image-250" title="vs2010" src="http://multi-crash.com/blog/wp-content/uploads/2010/05/vs2010.jpg" alt="" width="680" height="517" /></a></p>
<p><span id="more-249"></span>颇值得一提的是Edit菜单里多了一个Navigate To&#8230;，能快速索引到Symbol或者文件名，对于它我只想说，finally！终于可以安心告别Visual Assist了</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/05/vs2010-1.jpg"><img class="alignnone size-full wp-image-251" title="vs2010-1" src="http://multi-crash.com/blog/wp-content/uploads/2010/05/vs2010-1.jpg" alt="" width="680" height="517" /></a></p>
<p>据说这次的c++编译器被重写了很大一部分，对于新支持的一堆新特性我暂时还不太关心，更重要的是编译出来目标代码的质量。把<a title="sdf" href="http://multi-crash.com/?page_id=158" target="_blank"><span style="color: #0000ff;">Grandpa工程</span></a>转到2010下重编了下，结果得到的Grandpa.dll是215K，而之前vs2005编译出来的是252K。运行了一下Demo，得到的性能评估数据表明，测试模型的更新时间从约0.16ms缩短到了约0.14ms。目标代码的大小和运行速度都得到了10%以上的改善，结果令人满意。当然，我只是试了一个工程，不具有普遍参考意义。</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/05/vs2010-3.jpg"><img class="alignnone size-full wp-image-252" title="vs2010-3" src="http://multi-crash.com/blog/wp-content/uploads/2010/05/vs2010-3.jpg" alt="" width="617" height="88" /></a></p>
<p>希望vc 2010真的能象vc6一样成为下一个经典，另外如果能让我们能买得起就更好了（至少是Professional版）。</p>
]]></content:encoded>
			<wfw:commentRss>http://multi-crash.com/?feed=rss2&amp;p=249</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>我也来臭美一个:D</title>
		<link>http://multi-crash.com/?p=241</link>
		<comments>http://multi-crash.com/?p=241#comments</comments>
		<pubDate>Sun, 21 Mar 2010 15:41:46 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[音乐]]></category>

		<guid isPermaLink="false">http://multi-crash.com/?p=241</guid>
		<description><![CDATA[为什么要说“也”呢？呵呵 只会一首曲子，不过好几年了居然还没有忘 附送一首我小弟弹的:D]]></description>
			<content:encoded><![CDATA[<p>为什么要说“也”呢？呵呵</p>
<p>只会一首曲子，不过好几年了居然还没有忘<br />
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="400" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="align" value="middle" /><param name="src" value="http://player.youku.com/player.php/sid/XMTU5NDM1Njg4/v.swf" /><param name="quality" value="high" /><embed type="application/x-shockwave-flash" width="480" height="400" src="http://player.youku.com/player.php/sid/XMTU5NDM1Njg4/v.swf" quality="high" align="middle"></embed></object></p>
<p><span id="more-241"></span>附送一首我小弟弹的:D<br />
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="400" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="align" value="middle" /><param name="src" value="http://player.youku.com/player.php/sid/XMTUzOTIwMTI4/v.swf" /><param name="quality" value="high" /><embed type="application/x-shockwave-flash" width="480" height="400" src="http://player.youku.com/player.php/sid/XMTUzOTIwMTI4/v.swf" quality="high" align="middle"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://multi-crash.com/?feed=rss2&amp;p=241</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>又搞了个异形魔方</title>
		<link>http://multi-crash.com/?p=233</link>
		<comments>http://multi-crash.com/?p=233#comments</comments>
		<pubDate>Sat, 20 Mar 2010 14:08:44 +0000</pubDate>
		<dc:creator>zero</dc:creator>
				<category><![CDATA[游戏]]></category>
		<category><![CDATA[魔方]]></category>

		<guid isPermaLink="false">http://multi-crash.com/?p=233</guid>
		<description><![CDATA[四面的，买的时候挑了红黄蓝绿的颜色组合 这个是顶视图 和之前那个异形六面魔方异性六面魔方一样，弄乱了看起来很可怕 但是信不信由你，它其实仍然是个三阶魔方。不信可以从这个角度……看出来了吧，熟悉的九宫格布局。 悟出这个窍门以后就简单了，三阶魔方的大部分定式步骤都可以套用。只是需要一点时间来适应，因为很容易忘记哪个是你正在解决的“面”。熟悉了以后会发现，它甚至比普通三阶魔方还要简单，因为它的“角块”是没有方向的。]]></description>
			<content:encoded><![CDATA[<p>四面的，买的时候挑了红黄蓝绿的颜色组合</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/03/IMG_5425.jpg"><img class="alignnone size-full wp-image-238" title="IMG_5425" src="http://multi-crash.com/blog/wp-content/uploads/2010/03/IMG_5425.jpg" alt="" width="480" height="361" /></a></p>
<p><span id="more-233"></span></p>
<p>这个是顶视图</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/03/IMG_5426.jpg"><img class="alignnone size-full wp-image-237" title="IMG_5426" src="http://multi-crash.com/blog/wp-content/uploads/2010/03/IMG_5426.jpg" alt="" width="480" height="361" /></a></p>
<p>和之前那个<a href="http://multi-crash.com/?p=11" target="_blank"><span style="color: #0000ff;">异形六面魔方</span></a>异性六面魔方一样，弄乱了看起来很可怕</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/03/IMG_5427.jpg"><img class="alignnone size-full wp-image-236" title="IMG_5427" src="http://multi-crash.com/blog/wp-content/uploads/2010/03/IMG_5427.jpg" alt="" width="480" height="361" /></a></p>
<p>但是信不信由你，它其实仍然是个三阶魔方。不信可以从这个角度……看出来了吧，熟悉的九宫格布局。</p>
<p><a href="http://multi-crash.com/blog/wp-content/uploads/2010/03/IMG_54281.jpg"><img class="alignnone size-full wp-image-235" title="IMG_5428" src="http://multi-crash.com/blog/wp-content/uploads/2010/03/IMG_54281.jpg" alt="" width="480" height="361" /></a></p>
<p>悟出这个窍门以后就简单了，三阶魔方的大部分定式步骤都可以套用。只是需要一点时间来适应，因为很容易忘记哪个是你正在解决的“面”。熟悉了以后会发现，它甚至比普通三阶魔方还要简单，因为它的“角块”是没有方向的。</p>
]]></content:encoded>
			<wfw:commentRss>http://multi-crash.com/?feed=rss2&amp;p=233</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
