<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2415040221222523714</id><updated>2011-12-25T14:17:32.703+08:00</updated><category term='simplicity'/><category term='break point'/><category term='debug'/><category term='code snip'/><category term='encoding'/><category term='squirrel'/><category term='projective texture'/><category term='shader'/><category term='progressive'/><category term='reprojection cache'/><category term='syntax highlight'/><category term='hell'/><category term='philosophy'/><category term='SIMD'/><category term='blizzard'/><category term='smart pointer'/><category term='loading'/><category term='demo'/><category term='utf'/><category term='thread'/><category term='assao'/><category term='meta programming'/><category term='library'/><category term='starcraft'/><category term='shadow mapping'/><category term='visual studio'/><category term='unit test'/><category term='opengl'/><category term='plugin'/><category term='tips'/><category term='glsl'/><category term='weak pointer'/><category term='0x4160'/><category term='optimization'/><category term='design'/><category term='3ds'/><category term='performance'/><category term='ssao'/><category term='c++'/><title type='text'>還是簡單些好</title><subtitle type='html'>記錄我的編程新思維</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>47</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-6964515545659002039</id><published>2010-12-29T16:24:00.001+08:00</published><updated>2010-12-29T16:24:49.689+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='visual studio'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><title type='text'>Visual Studio 插件推介–metalscroll</title><content type='html'>&lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://mtlung.blogspot.com/2009/03/visual-studio-rockscroll.html"&gt;早前&lt;/a&gt;我曾經介紹過 &lt;a href="http://www.hanselman.com/blog/IntroducingRockScroll.aspx" target="_blank"&gt;RockScroll&lt;/a&gt; 這個插件，如今我找到了一個更好的代替品 &lt;a href="http://code.google.com/p/metalscroll/" target="_blank"&gt;metalscroll&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;它比起 RockScroll 有很多細節的改善，最重要是掛掉的情況沒有了。&lt;/p&gt;    &lt;p&gt;&lt;img src="http://i3.visualstudiogallery.msdn.microsoft.com/en-us/3a50c69b-9b35-4eb2-b06a-328758afcaa2/image/file/8113/0/smallscreenshot.png" /&gt;&lt;/p&gt;&lt;/blockquote&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-6964515545659002039?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/6964515545659002039/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2010/12/visual-studio-metalscroll.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6964515545659002039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6964515545659002039'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2010/12/visual-studio-metalscroll.html' title='Visual Studio 插件推介–metalscroll'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5413410761022988280</id><published>2010-02-24T12:11:00.001+08:00</published><updated>2010-02-24T15:33:46.367+08:00</updated><title type='text'>讓工作管理員進行繪圖</title><content type='html'>&lt;p&gt;興之所致，找來一個有趣面試題目來解答：如何能讓CPU 的使用率表現為一條正弦曲線？&lt;/p&gt;  &lt;p&gt;這個題目來自《&lt;a href="http://www.china-pub.com/38070" target="_blank"&gt;编程之美--微软技术面试心得&lt;/a&gt;》，有機會的我會話買下來當作娛樂。&lt;/p&gt;  &lt;pre class="brush: cpp"&gt;// This program try to burn cpu cycles with a sinusoid function against the time.&lt;br /&gt;#include &amp;lt;MCD/Core/System/TaskPool.h&amp;gt;&lt;br /&gt;#include &amp;lt;MCD/Core/System/Timer.h&amp;gt;&lt;br /&gt;#include &amp;lt;math.h&amp;gt;&lt;br /&gt;&lt;br /&gt;class Task : public MCD::TaskPool::Task {&lt;br /&gt;public:&lt;br /&gt;  Task(float frequency) : MCD::TaskPool::Task(0), mFrequency(frequency) {}&lt;br /&gt;&lt;br /&gt;  sal_override void run(MCD::Thread&amp;amp; thread) throw() {&lt;br /&gt;    MCD::Timer timer;&lt;br /&gt;    while(thread.keepRun()) {&lt;br /&gt;      double current = timer.get().asSecond();&lt;br /&gt;      double busy = (sin(current * mFrequency) + 1) / 2;&lt;br /&gt;      double idle = 1 - busy;  // We base on a 1 second interval&lt;br /&gt;      while(timer.get().asSecond() &amp;lt; current + busy) {}  // Burn CPU cycles&lt;br /&gt;      MCD::mSleep(int(idle * 1000));  // Let CPU idle&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // The Task instance is not needed any more, destroy it.&lt;br /&gt;    delete this;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  float mFrequency;&lt;br /&gt;};  // Task&lt;br /&gt;&lt;br /&gt;int main(int, char const*[])&lt;br /&gt;{&lt;br /&gt;  const size_t cpuCount = 4;  // Set this to match your hardware thread count&lt;br /&gt;  const float frequency = 0.1f;&lt;br /&gt;&lt;br /&gt;  MCD::TaskPool taskPool;&lt;br /&gt;  taskPool.setThreadCount(cpuCount);&lt;br /&gt;&lt;br /&gt;  for(size_t i=0; i&amp;lt;cpuCount; ++i)&lt;br /&gt;    taskPool.enqueue(*new Task(frequency));&lt;br /&gt;&lt;br /&gt;  std::cout &amp;lt;&amp;lt; &amp;quot;Press enter to quit...\n&amp;quot;;&lt;br /&gt;  std::cin.get();&lt;br /&gt;&lt;br /&gt;  taskPool.stop();&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_4CNEj03jaok/S4Sm_UOHRvI/AAAAAAAAAR4/wEnCuwR8nPI/s1600-h/sinusoidTaskManager%5B7%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="sinusoidTaskManager" border="0" alt="sinusoidTaskManager" src="http://lh6.ggpht.com/_4CNEj03jaok/S4SnANQZRxI/AAAAAAAAAR8/tLZR8xNKwOA/sinusoidTaskManager_thumb%5B5%5D.png?imgmax=800" width="315" height="360" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;&lt;p&gt;註：中間的那個高峰因突如其來的 MSN 短訊耗掉了整整一個 CPU Core 而缺了一角。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5413410761022988280?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5413410761022988280/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2010/02/blog-post.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5413410761022988280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5413410761022988280'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2010/02/blog-post.html' title='讓工作管理員進行繪圖'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_4CNEj03jaok/S4SnANQZRxI/AAAAAAAAAR8/tLZR8xNKwOA/s72-c/sinusoidTaskManager_thumb%5B5%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-2401792221131959111</id><published>2009-12-15T23:41:00.001+08:00</published><updated>2009-12-15T23:43:11.871+08:00</updated><title type='text'>Virtual box shared folders 備忘</title><content type='html'>&lt;p&gt;&lt;a href="http://www.virtualbox.org/wiki/Changelog" target="_blank"&gt;Virtual box 3.1&lt;/a&gt; 已支援 OpenGl 2.1，MCore 的 Linux port 也可繼續。&lt;/p&gt;  &lt;p&gt;已往我使用 smbfs 來 mount Windows 的網絡磁碟機：&lt;/p&gt;  &lt;p&gt;&lt;font color="#808080"&gt;//192.168.0.1/mcore3d /home/ricky/mcore3d smbfs iocharset=utf8,unicode,username=xxx,password=xxx 0 0&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;不過很容易會有權限或 encoding 的問題，而某個 library 更新之後又會有機會使原有設定失效。&lt;/p&gt;  &lt;p&gt;比較之下，Virtual box 提供的 shared folders 就可靠得多：&lt;/p&gt;  &lt;p&gt;&lt;font color="#808080"&gt;mcore3d /home/ricky/mcore3d vboxsf defaults 0 0&lt;/font&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-2401792221131959111?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/2401792221131959111/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/12/virtual-box-shared-folders.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2401792221131959111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2401792221131959111'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/12/virtual-box-shared-folders.html' title='Virtual box shared folders 備忘'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-9066104414388631049</id><published>2009-12-02T12:07:00.001+08:00</published><updated>2009-12-02T12:23:50.956+08:00</updated><title type='text'>為灰鼠捉蟲</title><content type='html'>&lt;p&gt;使用了 Squirrel 已好一些日子，終於給我遇到了臭蟲。這臭蟲的表徵是不確定的行為，debug/release/，run-with/without debugger 都會產生不同的結果。這一類不確定的行為不是多緒就是錯誤記憶體存取做成，而面對著陌生的 code base，最好的捉蟲方法就是使用專門的工具，如 &lt;a href="http://software.intel.com/en-us/intel-parallel-studio-home/" target="_blank"&gt;Intel Parallel Studio&lt;/a&gt; 提供的 &lt;a href="http://software.intel.com/en-us/intel-parallel-inspector/" target="_blank"&gt;Parallel Inspector&lt;/a&gt;。&lt;/p&gt;  &lt;p&gt;最終找到錯誤源於一處被 reference 的記憶體給 realloc 了；餘下的就交給 Squirrel 作者&lt;a href="http://squirrel-lang.org/forums/3585/ShowThread.aspx#3585" target="_blank"&gt;處理&lt;/a&gt;。&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Invalid memory access" border="0" alt="Invalid memory access" src="http://lh6.ggpht.com/_4CNEj03jaok/SxXn_96NkbI/AAAAAAAAAJM/rF5CU5MAGR8/Invalid%20memory%20access%5B9%5D.png?imgmax=800" width="562" height="484" /&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-9066104414388631049?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/9066104414388631049/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/12/blog-post.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/9066104414388631049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/9066104414388631049'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/12/blog-post.html' title='為灰鼠捉蟲'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_4CNEj03jaok/SxXn_96NkbI/AAAAAAAAAJM/rF5CU5MAGR8/s72-c/Invalid%20memory%20access%5B9%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-1306638685671309589</id><published>2009-10-21T18:31:00.001+08:00</published><updated>2009-10-21T18:31:03.677+08:00</updated><title type='text'>Squirrel remote debugger</title><content type='html'>&lt;p&gt;以下是完成了大約三分之一的 Squirrel 遠程除錯器。當中的製作過程比想像中簡單，皆因 Squirrel 已有一個除錯伺服器的實作 sqdbg，而我需要做的只是客戶端與 Gui。&lt;/p&gt;  &lt;p&gt;Sqdbg 的編程界面僅有四個函數，使用 Xml 來進行&lt;a href="http://wiki.squirrel-lang.org/default.aspx/SquirrelWiki/SQDBGProtocolReference.html"&gt;客戶端溝通&lt;/a&gt;。如今加減中斷點，下一行，跳出跳入和暫停繼續等等已經完成，接下來還有堆疊，變量監視等工夫。&lt;/p&gt;  &lt;p&gt;除了結合自家的遊戲引擎，它也會獨立出來貢獻給 Squirrel 社群。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_4CNEj03jaok/St7i5ARiSEI/AAAAAAAAAIs/D5z4oj6TOPk/s1600-h/SqdbgClient%5B3%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SqdbgClient" border="0" alt="SqdbgClient" src="http://lh6.ggpht.com/_4CNEj03jaok/St7i54CIptI/AAAAAAAAAIw/4eKbVKO5vQs/SqdbgClient_thumb%5B1%5D.png?imgmax=800" width="644" height="391" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-1306638685671309589?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/1306638685671309589/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/10/squirrel-remote-debugger.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/1306638685671309589'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/1306638685671309589'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/10/squirrel-remote-debugger.html' title='Squirrel remote debugger'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_4CNEj03jaok/St7i54CIptI/AAAAAAAAAIw/4eKbVKO5vQs/s72-c/SqdbgClient_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-7508936777541588956</id><published>2009-09-17T09:59:00.004+08:00</published><updated>2009-09-29T11:47:44.605+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><title type='text'>In-Game C++ Memory Profiler</title><content type='html'>&lt;p&gt;忙碌了兩個多月，這篇有可出現在 Gems 系列的 "In-Game Memory Profiler" 終於完成。製作這類 Profiler 已經不知多少次了，但這次是最滿意的；可以媲美需付款的產品 (其實類似的 C++ memory profiler 產品真的少之又少)，甚至更好。就是它，讓我知道 &lt;a href="http://www.bulletphysics.com/wordpress/"&gt;Bullet&lt;/a&gt; 每次 step simulation 都有好幾千個即用即棄的 memory allocation/de-allocation；後來我把它的 default pool size 增大，問題也就解決了。&lt;/p&gt;  &lt;p&gt;&lt;img style="border-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" title="In-Game-Memory-Profiler" alt="In-Game-Memory-Profiler" src="http://lh5.ggpht.com/_4CNEj03jaok/SrHLZDkzqPI/AAAAAAAAAIo/uV6d0p4mEpM/InGameMemoryProfiler%5B1%5D.png?imgmax=800" border="0" height="657" width="683" /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-7508936777541588956?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/7508936777541588956/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/09/in-game-memory-profiler.html#comment-form' title='6 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7508936777541588956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7508936777541588956'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/09/in-game-memory-profiler.html' title='In-Game C++ Memory Profiler'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_4CNEj03jaok/SrHLZDkzqPI/AAAAAAAAAIo/uV6d0p4mEpM/s72-c/InGameMemoryProfiler%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-3212940245534629561</id><published>2009-09-05T14:32:00.000+08:00</published><updated>2009-09-05T15:50:50.019+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='simplicity'/><category scheme='http://www.blogger.com/atom/ns#' term='weak pointer'/><category scheme='http://www.blogger.com/atom/ns#' term='smart pointer'/><title type='text'>弱指針的重要性</title><content type='html'>雖然有許多有關智能指針 (&lt;a href="http://www.codeproject.com/KB/stl/boostsmartptr.aspx"&gt;Smart pointer&lt;/a&gt;) 的文章和範例，但關於&lt;a href="http://home.clara.net/raoulgough/weak_ptr/index.html"&gt;弱指針&lt;/a&gt;的就相對寥寥無幾。縱然有所提及，也只是草草幾筆說弱指針是用來避免 &amp;quot;循環引用 (&lt;a href="http://avhacker.blogspot.com/2007/07/cyclic-dependency-memory-leak-issue.html"&gt;Cyclic reference&lt;/a&gt;)&amp;quot; 云云。就此問題我嘗試用一些例子去補充說明弱指針何時用，為什麼用。  &lt;br /&gt;  &lt;br /&gt;首先讓我們看一個遊戲引擎中典型的資料管理系統例子：  &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4CNEj03jaok/Sb0Tmq27k4I/AAAAAAAAAF4/X9ZZLkZt-wA/s1600-h/weakPtrExample1.png"&gt;&lt;img style="width: 400px; height: 233px; cursor: pointer" id="BLOGGER_PHOTO_ID_5313424690427696002" border="0" alt="" src="http://1.bp.blogspot.com/_4CNEj03jaok/Sb0Tmq27k4I/AAAAAAAAAF4/X9ZZLkZt-wA/s400/weakPtrExample1.png" /&gt;&lt;/a&gt;  &lt;br /&gt;因為要節省內存的需求，Texture (紋理) 被設計成能夠給多個 Model (三維模型) &lt;span style="font-weight: bold"&gt;共享&lt;/span&gt;。而 Texture 的&lt;span style="font-weight: bold"&gt;存在目的&lt;/span&gt;，就是留給 Model 作為繪畫時使用；明顯地，Model 可用 Smart Pointer 來引用 Texture；相對地 NameToTextureMap (方便用名稱來作資料搜尋) 該使用甚麼來引用 Texture 就沒有那麼明顯了。先看看使用不同的指針會有什麼的後果吧。  &lt;br /&gt;  &lt;ul&gt;   &lt;li&gt;裸指針 - 這將會是災難性的選擇，因為一旦再沒有 Model 引用 Texture， NameToTextureMap 就會在&lt;span style="font-weight: bold"&gt;毫無知會&lt;/span&gt;的情況下指向一件非法的物件。      &lt;br /&gt;      &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;智能指針 - 為了解決裸指針的問題，選擇智能指針像是合理的做法。不過請大家想一想，一旦 NameToTextureMap 引用了 Texture 以後，&lt;span style="font-weight: bold"&gt;何時才可刪除引用&lt;/span&gt;呢？假如不去刪除引用，Texture 的生命期就會與 NameToTextureMap 同步化，失去了原先使用智能指針作為純粹被 Model 共享的意義。&lt;/li&gt; &lt;/ul&gt; 這時就是讓弱指針發揮的時候。弱指針的特性就是當它指向的物件被銷毀之時，弱指針的值就會瞬間變為零；因此，讓 NameToTextureMap 使用弱指針，再定期把零值的弱指針由容器中刪除，以上的 UML 設計就可忠實地實作出來。  &lt;br /&gt;  &lt;br /&gt;總結來說，智能指針旨不在於提供程序員不加思索就可使用的工具，它是一種能幫助你正確地實行擁有權共享的工具。就算是聲稱&lt;span style="font-weight: bold"&gt;擁有自動內存管理的語言&lt;/span&gt;如 Java, C#, Python, Ruby 等等都一定會有弱指針這個概念，否則內存洩漏是很難避免的。假若你在開發 C++ 程序的時候遇到許多關於物件擁有權的問題，請記得弱指針的存在。  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-3212940245534629561?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/3212940245534629561/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/03/blog-post_04.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3212940245534629561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3212940245534629561'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/03/blog-post_04.html' title='弱指針的重要性'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_4CNEj03jaok/Sb0Tmq27k4I/AAAAAAAAAF4/X9ZZLkZt-wA/s72-c/weakPtrExample1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5348716988912548209</id><published>2009-09-03T10:07:00.003+08:00</published><updated>2009-09-03T10:34:34.665+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='syntax highlight'/><title type='text'>Syntax highlight in Notepad++</title><content type='html'>從互聯網找到個給 &lt;a href="http://mtlung.blogspot.com/2009/04/squirrel.html"&gt;Squirrel&lt;/a&gt; 語言著色的 Notepad++ config 檔案，也可作為日後 &lt;a href="http://mtlung.blogspot.com/2009/06/blog-post_21.html"&gt;MCore3D Studio&lt;/a&gt; 結合 &lt;a href="http://scintilla.org/"&gt;Scintilla Control&lt;/a&gt; 的藍圖。把以下的 Xml 儲存為 "userDefineLang.xml" 再放入 Notepad++ 的安置目錄就行了。&lt;br /&gt;&lt;pre class="brush: xml"&gt;&lt;br /&gt;&amp;lt;NotepadPlus&amp;gt;&lt;br /&gt;   &amp;lt;UserLang name="Squirrel" ext="nut"&amp;gt;&lt;br /&gt;       &amp;lt;Settings&amp;gt;&lt;br /&gt;           &amp;lt;Global caseIgnored="no" /&amp;gt;&lt;br /&gt;           &amp;lt;TreatAsSymbol comment="yes" commentLine="yes" /&amp;gt;&lt;br /&gt;           &amp;lt;Prefix words1="no" words2="no" words3="no" words4="no" /&amp;gt;&lt;br /&gt;       &amp;lt;/Settings&amp;gt;&lt;br /&gt;       &amp;lt;KeywordLists&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Delimiters"&amp;gt;&amp;amp;quot;00&amp;amp;quot;00&amp;lt;/Keywords&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Folder+"&amp;gt;&amp;lt;/Keywords&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Folder-"&amp;gt;&amp;lt;/Keywords&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Operators"&amp;gt;- ! ( ) , . : ; ? [ ] { } + &amp;amp;lt; = &amp;amp;gt;&amp;lt;/Keywords&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Comment"&amp;gt;1/* 2*/ 0//&amp;lt;/Keywords&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Words1"&amp;gt;break case catch class clone continue const default delegate delete else enum extends for function if in null resume return switch this throw try typeof parent yield constructor instanceof true false static do while foreach&amp;lt;/Keywords&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Words2"&amp;gt;local&amp;lt;/Keywords&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Words3"&amp;gt;ARGS&amp;lt;/Keywords&amp;gt;&lt;br /&gt;           &amp;lt;Keywords name="Words4"&amp;gt;&amp;lt;/Keywords&amp;gt;&lt;br /&gt;       &amp;lt;/KeywordLists&amp;gt;&lt;br /&gt;       &amp;lt;Styles&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="DEFAULT" styleID="11" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="FOLDEROPEN" styleID="12" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="FOLDERCLOSE" styleID="13" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="KEYWORD1" styleID="5" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="1" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="KEYWORD2" styleID="6" fgColor="006262" bgColor="FFFFFF" fontName="" fontStyle="1" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="KEYWORD3" styleID="7" fgColor="007777" bgColor="FFFFFF" fontName="" fontStyle="3" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="KEYWORD4" styleID="8" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="COMMENT" styleID="1" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="2" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="COMMENT LINE" styleID="2" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="2" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="NUMBER" styleID="4" fgColor="FF8000" bgColor="FFFFFF" fontName="" fontStyle="0" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="OPERATOR" styleID="10" fgColor="000080" bgColor="FFFFFF" fontName="" fontStyle="1" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="DELIMINER1" styleID="14" fgColor="7E7E7E" bgColor="FFFFFF" fontName="" fontStyle="2" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="DELIMINER2" styleID="15" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" /&amp;gt;&lt;br /&gt;           &amp;lt;WordsStyle name="DELIMINER3" styleID="16" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" /&amp;gt;&lt;br /&gt;       &amp;lt;/Styles&amp;gt;&lt;br /&gt;   &amp;lt;/UserLang&amp;gt;&lt;br /&gt;&amp;lt;/NotepadPlus&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5348716988912548209?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5348716988912548209/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/09/syntax-highlight-in-notepad.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5348716988912548209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5348716988912548209'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/09/syntax-highlight-in-notepad.html' title='Syntax highlight in Notepad++'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5707607436276415044</id><published>2009-08-29T23:19:00.001+08:00</published><updated>2009-08-30T00:30:50.940+08:00</updated><title type='text'>Mesa 3D</title><content type='html'>&lt;p&gt;多年沒有注視的 &lt;a href="http://www.mesa3d.org/"&gt;Mesa 3D&lt;/a&gt; 原來已經支援 OpenGL 2.1，亦表示它包含了 Glsl 的編輯器與運行期軟件。但更令我感到驚喜的是它的驅動層是可以使用 Direct3D9 作為實作。這個實作應該是基於 &lt;a href="http://majorgeeks.com/GLDirect_d381.html"&gt;GlDirect&lt;/a&gt; 為藍圖，把一個個 OpenGL 指令變換成 Direct3D 的函數。     &lt;br /&gt;理想地，我不必再為不同平台間的 Graphics API 而煩惱；因為 OpenGL 本身就是設計成可擴充的，而 Mesa3D 又提供了一個很好的起步點。我的引擎只要沿用 OpenGL，再對 Mesa3D 的驅動層稍為修改就應該可在 XBox 上跑。     &lt;br /&gt;這兩天會花點時間對這想法的實際可行性多作研究。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_4CNEj03jaok/SplG61EKVYI/AAAAAAAAAII/T5jo1tXTgWs/s1600-h/image%5B6%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_4CNEj03jaok/SplG7cjC7wI/AAAAAAAAAIM/KOvwDcFT8jg/image_thumb%5B4%5D.png?imgmax=800" width="404" height="265" /&gt;&lt;/a&gt;&lt;a href="http://lh6.ggpht.com/_4CNEj03jaok/SplXtsuWTOI/AAAAAAAAAIQ/oGy332kcCOo/s1600-h/image%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_4CNEj03jaok/SplXuQh2tkI/AAAAAAAAAIU/9E4rosiKoGk/image_thumb.png?imgmax=800" width="244" height="234" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5707607436276415044?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5707607436276415044/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/08/mesa-3d.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5707607436276415044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5707607436276415044'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/08/mesa-3d.html' title='Mesa 3D'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_4CNEj03jaok/SplG7cjC7wI/AAAAAAAAAIM/KOvwDcFT8jg/s72-c/image_thumb%5B4%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-1912786023280747736</id><published>2009-08-05T14:36:00.014+08:00</published><updated>2009-08-06T10:35:06.149+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='code snip'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><title type='text'>Restrict 指針</title><content type='html'>一般時候我們的編譯器都不知道兩個指針所指的位置是否相同或者有所重疊，大大降低了它的優化效果。就拿 Matrix3 * Vector3 為例：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;void Matrix::MulVector(const Vec3&amp;amp; v, Vec3&amp;amp; result) {&lt;br /&gt; result.x = m00 * v.x + m01 * v.y + m02 * v.z;&lt;br /&gt; result.y = m10 * v.x + m11 * v.y + m12 * v.z;&lt;br /&gt; result.z = m20 * v.x + m21 * v.y + m22 * v.z;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;很簡單的三行代碼，然而它隱藏了一個效能上的問題。堂 result 被賦予了新的值後，編譯器認為 v 的值也可能被更改了 (v 和 result 也是 reference，pointer 的一類)。因此原本可以留在 register 裡的 v.x, v.y 和 v.z 被迫從新由記憶體裡閱讀回來。看看VC2008 編譯成的機器碼吧：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;result.x = m00 * v.x + m01 * v.y + m02 * v.z;&lt;br /&gt;mov         eax,dword ptr [esp+4]&lt;br /&gt;movss       xmm0,dword ptr [ecx+8]&lt;br /&gt;mulss       xmm0,dword ptr [eax+8]&lt;br /&gt;movss       xmm1,dword ptr [ecx+4]&lt;br /&gt;mulss       xmm1,dword ptr [eax+4]&lt;br /&gt;mov         edx,dword ptr [esp+8]&lt;br /&gt;addss       xmm0,xmm1&lt;br /&gt;movss       xmm1,dword ptr [eax]&lt;br /&gt;mulss       xmm1,dword ptr [ecx]&lt;br /&gt;addss       xmm0,xmm1&lt;br /&gt;movss       dword ptr [edx],xmm0&lt;br /&gt;&lt;br /&gt;result.y = m10 * v.x + m11 * v.y + m12 * v.z;&lt;br /&gt;movss       xmm0,dword ptr [ecx+0Ch]&lt;br /&gt;mulss       xmm0,dword ptr [eax]&lt;br /&gt;movss       xmm1,dword ptr [ecx+14h]&lt;br /&gt;mulss       xmm1,dword ptr [eax+8]&lt;br /&gt;addss       xmm0,xmm1&lt;br /&gt;movss       xmm1,dword ptr [ecx+10h]&lt;br /&gt;mulss       xmm1,dword ptr [eax+4]&lt;br /&gt;addss       xmm0,xmm1&lt;br /&gt;movss       dword ptr [edx+4],xmm0&lt;br /&gt;&lt;br /&gt;result.z = m20 * v.x + m21 * v.y + m22 * v.z;&lt;br /&gt;movss       xmm0,dword ptr [ecx+18h]&lt;br /&gt;mulss       xmm0,dword ptr [eax]&lt;br /&gt;movss       xmm1,dword ptr [ecx+20h]&lt;br /&gt;mulss       xmm1,dword ptr [eax+8]&lt;br /&gt;addss       xmm0,xmm1&lt;br /&gt;movss       xmm1,dword ptr [ecx+1Ch]&lt;br /&gt;mulss       xmm1,dword ptr [eax+4]&lt;br /&gt;addss       xmm0,xmm1&lt;br /&gt;movss       dword ptr [edx+8],xmm0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;這時候使用 restrict 就可幫上編譯器把。請注意，VC2008 的 __restrict 只對指針生效：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;void Matrix::MulVector(const Vec3&amp;amp; v_, Vec3&amp;amp; result_) {&lt;br /&gt; const Vec3* __restrict v = &amp;amp;v_;&lt;br /&gt; Vec3* __restrict ret = &amp;amp;result_;&lt;br /&gt; result-&gt;x = m00 * v-&gt;x + m01 * v-&gt;y + m02 * v-&gt;z;&lt;br /&gt; result-&gt;y = m10 * v-&gt;x + m11 * v-&gt;y + m12 * v-&gt;z;&lt;br /&gt; result-&gt;z = m20 * v-&gt;x + m21 * v-&gt;y + m22 * v-&gt;z;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;從新編譯後的機器碼：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;result-&gt;x = m00 * v-&gt;x + m01 * v-&gt;y + m02 * v-&gt;z;&lt;br /&gt;mov         eax,dword ptr [esp+4]&lt;br /&gt;movss       xmm1,dword ptr [eax+4]&lt;br /&gt;movss       xmm0,dword ptr [eax+8]&lt;br /&gt;movss       xmm2,dword ptr [eax]&lt;br /&gt;movss       xmm3,dword ptr [ecx+4]&lt;br /&gt;movss       xmm4,dword ptr [ecx+8]&lt;br /&gt;mov         eax,dword ptr [esp+8]&lt;br /&gt;mulss       xmm3,xmm1&lt;br /&gt;mulss       xmm4,xmm0&lt;br /&gt;addss       xmm3,xmm4&lt;br /&gt;movaps      xmm4,xmm2&lt;br /&gt;mulss       xmm4,dword ptr [ecx]&lt;br /&gt;addss       xmm3,xmm4&lt;br /&gt;&lt;br /&gt;result-&gt;y = m10 * v-&gt;x + m11 * v-&gt;y + m12 * v-&gt;z;&lt;br /&gt;movss       xmm4,dword ptr [ecx+10h]&lt;br /&gt;movss       dword ptr [eax],xmm3&lt;br /&gt;movss       xmm3,dword ptr [ecx+0Ch]&lt;br /&gt;mulss       xmm3,xmm2&lt;br /&gt;mulss       xmm4,xmm1&lt;br /&gt;addss       xmm3,xmm4&lt;br /&gt;movss       xmm4,dword ptr [ecx+14h]&lt;br /&gt;mulss       xmm4,xmm0&lt;br /&gt;addss       xmm3,xmm4&lt;br /&gt;movss       dword ptr [eax+4],xmm3&lt;br /&gt;&lt;br /&gt;result-&gt;z = m20 * v-&gt;x + m21 * v-&gt;y + m22 * v-&gt;z;&lt;br /&gt;movss       xmm3,dword ptr [ecx+18h]&lt;br /&gt;mulss       xmm3,xmm2&lt;br /&gt;movss       xmm2,dword ptr [ecx+1Ch]&lt;br /&gt;mulss       xmm2,xmm1&lt;br /&gt;movss       xmm1,dword ptr [ecx+20h]&lt;br /&gt;addss       xmm3,xmm2&lt;br /&gt;mulss       xmm1,xmm0&lt;br /&gt;addss       xmm3,xmm1&lt;br /&gt;movss       dword ptr [eax+8],xmm3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;可以看到記憶體閱讀操作減少了。&lt;br /&gt;&lt;br /&gt;到最後，其實使用局部變量也可達到類似效果：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;void Matrix::MulVector(const Vec3&amp;amp; v, Vec3&amp;amp; result) {&lt;br /&gt; const float x = v.x;&lt;br /&gt; const float y = v.y;&lt;br /&gt; const float z = v.z;&lt;br /&gt;&lt;br /&gt; result.x = m00 * x + m01 * y + m02 * z;&lt;br /&gt; result.y = m10 * x + m11 * y + m12 * z;&lt;br /&gt; result.z = m20 * x + m21 * y + m22 * z;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-1912786023280747736?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/1912786023280747736/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/08/restrict.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/1912786023280747736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/1912786023280747736'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/08/restrict.html' title='Restrict 指針'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-3356260447858554794</id><published>2009-06-21T11:34:00.006+08:00</published><updated>2009-06-21T15:11:14.973+08:00</updated><title type='text'>引擎開發進度</title><content type='html'>有了新成員的加盟，遊戲引擎的開發進程加快，以下是一些測試程式的截圖。&lt;br /&gt;&lt;ul&gt;&lt;li&gt;漂亮又便宜的 God Ray&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/Sj3Wy4_ZRBI/AAAAAAAAAHo/jRh7YGEWjlQ/s1600-h/god+ray.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 311px;" src="http://4.bp.blogspot.com/_4CNEj03jaok/Sj3Wy4_ZRBI/AAAAAAAAAHo/jRh7YGEWjlQ/s400/god+ray.jpg" alt="" id="BLOGGER_PHOTO_ID_5349668102166103058" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Cube map + Normal mapping&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/Sj3W4UTtg2I/AAAAAAAAAHw/JistUabs-uc/s1600-h/normal+map+test.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 311px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/Sj3W4UTtg2I/AAAAAAAAAHw/JistUabs-uc/s400/normal+map+test.JPG" alt="" id="BLOGGER_PHOTO_ID_5349668195398419298" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;使用 Bullet 製作的 Physics component&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/Sj3W9AvCkHI/AAAAAAAAAH4/PivPneSph0M/s1600-h/bullet+physics+component+test.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 311px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/Sj3W9AvCkHI/AAAAAAAAAH4/PivPneSph0M/s400/bullet+physics+component+test.jpg" alt="" id="BLOGGER_PHOTO_ID_5349668276043681906" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;場景編輯器&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4CNEj03jaok/Sj3WrFwdKnI/AAAAAAAAAHg/8bjTbn7FyF8/s1600-h/studio-27-05-2009.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 279px;" src="http://1.bp.blogspot.com/_4CNEj03jaok/Sj3WrFwdKnI/AAAAAAAAAHg/8bjTbn7FyF8/s400/studio-27-05-2009.jpg" alt="" id="BLOGGER_PHOTO_ID_5349667968154151538" border="0" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-3356260447858554794?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/3356260447858554794/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/06/blog-post_21.html#comment-form' title='10 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3356260447858554794'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3356260447858554794'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/06/blog-post_21.html' title='引擎開發進度'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/Sj3Wy4_ZRBI/AAAAAAAAAHo/jRh7YGEWjlQ/s72-c/god+ray.jpg' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-2626961929246620519</id><published>2009-06-17T09:21:00.006+08:00</published><updated>2009-06-17T10:46:44.794+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='visual studio'/><category scheme='http://www.blogger.com/atom/ns#' term='thread'/><category scheme='http://www.blogger.com/atom/ns#' term='debug'/><category scheme='http://www.blogger.com/atom/ns#' term='break point'/><title type='text'>於特定線程設置中斷點</title><content type='html'>正在開發一個 memory profiler，為了針對多線程部分的除錯，我須要設置一個只對特定線程生效的中斷點。在 Visual Studio 中，有兩個非常類似的方法可達到目的：&lt;ol&gt;&lt;li&gt;右擊您的 break point 然後使用 break point filter&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/SjhRMOOYhLI/AAAAAAAAAHA/NAqxeXB7xlc/s1600-h/Breakpoint+filter.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://3.bp.blogspot.com/_4CNEj03jaok/SjhRMOOYhLI/AAAAAAAAAHA/NAqxeXB7xlc/s320/Breakpoint+filter.png" alt="" id="BLOGGER_PHOTO_ID_5348113827921167538" border="0" /&gt;&lt;/a&gt;選擇線程 id&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SjhRn9deQgI/AAAAAAAAAHI/FY-qF5gzEcg/s1600-h/Breakpoint+filter2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 273px;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SjhRn9deQgI/AAAAAAAAAHI/FY-qF5gzEcg/s320/Breakpoint+filter2.png" alt="" id="BLOGGER_PHOTO_ID_5348114304457392642" border="0" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;另一個方法是使用 break point condition&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SjhXPsHj0xI/AAAAAAAAAHQ/7vFMOQyb7ts/s1600-h/Breakpoint+filter3.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SjhXPsHj0xI/AAAAAAAAAHQ/7vFMOQyb7ts/s320/Breakpoint+filter3.png" alt="" id="BLOGGER_PHOTO_ID_5348120484554986258" border="0" /&gt;&lt;br /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/SjhX05Q5uRI/AAAAAAAAAHY/eqsKspzKUkc/s1600-h/Breakpoint+filter4.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 160px;" src="http://3.bp.blogspot.com/_4CNEj03jaok/SjhX05Q5uRI/AAAAAAAAAHY/eqsKspzKUkc/s320/Breakpoint+filter4.png" alt="" id="BLOGGER_PHOTO_ID_5348121123738990866" border="0" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-2626961929246620519?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/2626961929246620519/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/06/blog-post.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2626961929246620519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2626961929246620519'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/06/blog-post.html' title='於特定線程設置中斷點'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_4CNEj03jaok/SjhRMOOYhLI/AAAAAAAAAHA/NAqxeXB7xlc/s72-c/Breakpoint+filter.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-3436744553000196532</id><published>2009-06-06T08:51:00.007+08:00</published><updated>2009-06-06T15:03:35.746+08:00</updated><title type='text'>不要被 Windows.h 強暴</title><content type='html'>引擎的物理部份開始開發，我滿心喜悅地把 &lt;a href="http://continuousphysics.com/Bullet/"&gt;Bullet Pyhsics Engine&lt;/a&gt; 加入專案，但"咚"的一聲告訴我編譯器掛丟了。誰在作怪呢？當編譯器指向那一向相安無事的數學 max 函數，我就知道是 Bullet include 了 Windows.h。錯就錯在 Bullet 在 btQuickprof.h 哪裡包括了 Windows.h，迫使其他檔案也一併把 Windows.h include 過來，本來潔淨的命名空間就這樣被成千上萬的 macros 如 min, max, CreateWindow, DrawText 等等污染了。&lt;br /&gt;其實 Windows.h 是可以避免在標頭檔中出現的：&lt;br /&gt;&lt;ul&gt;&lt;li&gt;盡可能把函數定義由標頭檔移到源碼檔去&lt;/li&gt;&lt;li&gt;大多數基本型別如 DWORD, LARGE_INTEGER 等在標頭檔中可用 unsigned long,  __int64 來取代，再在源碼檔用 reinterpret_cast 返回原本的型別。大多數的 struct 指針都可以前置聲明(forward declarate) 如 typedef struct _GUID GUID;&lt;/li&gt;&lt;li&gt;更甚者還會使用 char mMutex[24]; 來取代 CRITICAL_SECTION mMutex; 再加上&lt;a href="http://www.skynet.ie/%7Ecaolan/Fragments/C++StaticAssert.html"&gt;編譯期 assert&lt;/a&gt; 確保兩者內存大小匹配：STATIC_ASSERT(sizeof(mMutex) == sizeof(CRITICAL_SECTION));&lt;/li&gt;&lt;/ul&gt;把 Windows.h 由標頭檔中抽離以後，不單止命名空間的問題解決了，連編譯的速度都快了哩!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-3436744553000196532?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/3436744553000196532/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/06/windowsh.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3436744553000196532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3436744553000196532'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/06/windowsh.html' title='不要被 Windows.h 強暴'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5321829307208613277</id><published>2009-05-30T17:05:00.010+08:00</published><updated>2009-05-30T21:49:21.184+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='squirrel'/><category scheme='http://www.blogger.com/atom/ns#' term='unit test'/><title type='text'>Squirrel 單元測試框架</title><content type='html'>暫時還無法找到一個給予 &lt;a href="http://mtlung.blogspot.com/2009/04/squirrel.html"&gt;Squirrel&lt;/a&gt; 使用的單元測試框架，唯有自己做吧。&lt;a href="http://www.lua.org/"&gt;Lua&lt;/a&gt; 的 &lt;a href="http://luaforge.net/projects/luaunit/"&gt;luaunit&lt;/a&gt; 是一個好的起點，不需一天的時間就把它移到 Squirrel，&lt;span class="definition"&gt;且&lt;/span&gt;給它命名為 &lt;a href="http://mtlung.googlepages.com/squnit.nut"&gt;squnit&lt;/a&gt; (&lt;a href="http://mtlung.googlepages.com/squnit.7z"&gt;下載&lt;/a&gt; / &lt;a href="http://mtlung.googlepages.com/squnit.7z"&gt;Download&lt;/a&gt;)。以下是一些使用範例：&lt;br /&gt;&lt;pre class="brush: sq"&gt;&lt;br /&gt;dofile("squnit.nut", true);&lt;br /&gt;&lt;br /&gt;class TestToto&lt;br /&gt;{&lt;br /&gt; a = null;&lt;br /&gt; s = null;&lt;br /&gt;&lt;br /&gt; function setUp()&lt;br /&gt; {&lt;br /&gt;  // Set up tests&lt;br /&gt;  a = 1;&lt;br /&gt;  s = "hop";&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function test1_withFailure()&lt;br /&gt; {&lt;br /&gt;  print("some stuff test 1\n");&lt;br /&gt;  assertEquals(a , 1);&lt;br /&gt;  // Will fail&lt;br /&gt;  assertEquals(a , 2);&lt;br /&gt;  assertEquals(a , 2);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function test2_withFailure()&lt;br /&gt; {&lt;br /&gt;  print("some stuff test 2\n");&lt;br /&gt;  assertEquals(a , 1);&lt;br /&gt;  assertEquals(s , "hop");&lt;br /&gt;  // Will fail&lt;br /&gt;  assertEquals(s , "bof");&lt;br /&gt;  assertEquals(s , "bof");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function test3()&lt;br /&gt; {&lt;br /&gt;  print("some stuff test 3\n");&lt;br /&gt;  assertEquals(a , 1);&lt;br /&gt;  assertEquals(s , "hop");&lt;br /&gt;  assertEquals(typeof a, "integer");&lt;br /&gt;  assertClose(0.01, -0.01, 0.02);&lt;br /&gt; }&lt;br /&gt;} // TestToto&lt;br /&gt;&lt;br /&gt;class TestTiti&lt;br /&gt;{&lt;br /&gt; a = null;&lt;br /&gt; s = null;&lt;br /&gt;&lt;br /&gt; function setUp()&lt;br /&gt; {&lt;br /&gt;  a = 1;&lt;br /&gt;  s = "hop";&lt;br /&gt;     print("TestTiti.setUp\n");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function tearDown()&lt;br /&gt; {&lt;br /&gt;  // Some tearDown() code if necessary&lt;br /&gt;  print("tearDown\n");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function test1_withFailure()&lt;br /&gt; {&lt;br /&gt;  print("some stuff test 1\n");&lt;br /&gt;  assertEquals(a , 1);&lt;br /&gt;  // Will fail&lt;br /&gt;  assertEquals(a , 2);&lt;br /&gt;  assertEquals(a , 2);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function test2_withFailure()&lt;br /&gt; {&lt;br /&gt;  print("some stuff test 2\n");&lt;br /&gt;  assertEquals(a , 1);&lt;br /&gt;  assertEquals(s , "hop");&lt;br /&gt;  // Will fail&lt;br /&gt;  assertEquals(s , "bof");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function test3()&lt;br /&gt; {&lt;br /&gt;  print("some stuff test 3\n");&lt;br /&gt;  assertEquals(a , 1);&lt;br /&gt;  assertEquals(s , "hop");&lt;br /&gt; }&lt;br /&gt;} // TestTiti&lt;br /&gt;&lt;br /&gt;// Simple test functions that were written previously can be integrated in luaunit too&lt;br /&gt;function test1_withFailure()&lt;br /&gt;{&lt;br /&gt; assert(1 == 1);&lt;br /&gt; // Will fail&lt;br /&gt; assert(1 == 2);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function test2_withFailure()&lt;br /&gt;{&lt;br /&gt; assert("a" == "a");&lt;br /&gt; // Will fail&lt;br /&gt; assert("a" == "b");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function test3()&lt;br /&gt;{&lt;br /&gt; assert(1 == 1);&lt;br /&gt; assert("a" == "a");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;TestFunctions &amp;lt;- wrapFunctions("test1_withFailure", "test2_withFailure", "test3");&lt;br /&gt;&lt;br /&gt;// SqUnit().run("test2_withFailure"); // Run only one test function&lt;br /&gt;// SqUnit().run("test1_withFailure");&lt;br /&gt;// SqUnit().run("TestToto"); // Run only on test class&lt;br /&gt;// SqUnit().run("TestTiti:test3"); // Run only one test method of a test class&lt;br /&gt;SqUnit().run(); // Run all tests&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;還可以使用 squnit 本身來測試自己：&lt;br /&gt;&lt;pre class="brush: squirrel"&gt;&lt;br /&gt;/* testSqunit.nut&lt;br /&gt; Description: Tests for the squnit testing framework&lt;br /&gt; Author: Ricky Lung (http://mtlung.blogspot.com/)&lt;br /&gt; Version: 1.0&lt;br /&gt;&lt;br /&gt; License: X11 License&lt;br /&gt;&lt;br /&gt; This set of files is published under the X11 License. You can do&lt;br /&gt; more or less anything you want to do with it.&lt;br /&gt;&lt;br /&gt; Permission is hereby granted, free of charge, to any person obtaining a copy&lt;br /&gt; of this software and associated documentation files (the "Software"), to deal&lt;br /&gt; in the Software without restriction, including without limitation the rights&lt;br /&gt; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&lt;br /&gt; copies of the Software, and to permit persons to whom the Software is&lt;br /&gt; furnished to do so, subject to the following conditions:&lt;br /&gt;&lt;br /&gt; The above copyright notice and this permission notice shall be included in all&lt;br /&gt; copies or substantial portions of the Software.&lt;br /&gt;&lt;br /&gt; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&lt;br /&gt; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&lt;br /&gt; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X&lt;br /&gt; CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN&lt;br /&gt; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION&lt;br /&gt; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;// This is a bit tricky since the test uses the features that it tests.&lt;br /&gt;&lt;br /&gt;dofile("squnit.nut", true);&lt;br /&gt;&lt;br /&gt;class TestSqUnit&lt;br /&gt;{&lt;br /&gt; function mypcall(func, ...)&lt;br /&gt; {&lt;br /&gt;  local args = array(0);&lt;br /&gt;  for(local i=0; i&amp;lt;vargc; ++i)&lt;br /&gt;   args.push(vargv[i]);&lt;br /&gt;&lt;br /&gt;  try {&lt;br /&gt;   func.pacall(args);&lt;br /&gt;   return true;&lt;br /&gt;  } catch(e) {&lt;br /&gt;   return false;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function testAssertError()&lt;br /&gt; {&lt;br /&gt;  local has_error = !mypcall(error, null, "coucou");&lt;br /&gt;  assert(has_error == true);&lt;br /&gt;  assertError(error, null, "coucou");&lt;br /&gt;  has_error = !mypcall(assertError, null, error, null, "coucou");&lt;br /&gt;  assert(has_error == false);&lt;br /&gt;&lt;br /&gt;  local f = function() {}&lt;br /&gt;  has_error = !mypcall(f, null);&lt;br /&gt;  assert(has_error == false);&lt;br /&gt;  has_error = !mypcall(assertError, null, f, null);&lt;br /&gt;  assert(has_error == true);&lt;br /&gt;&lt;br /&gt;  // multiple arguments&lt;br /&gt;  local multif = function(a, b, c) {&lt;br /&gt;   if(a == b &amp;amp;&amp;amp; b == c) return;&lt;br /&gt;   error("three arguments not equal");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  assertError(multif, null, 1, 1, 3);&lt;br /&gt;  assertError(multif, null, 1, 3, 1);&lt;br /&gt;  assertError(multif, null, 3, 1, 1);&lt;br /&gt;&lt;br /&gt;  has_error = !mypcall(assertError, null, multif, null, 1, 1, 1);&lt;br /&gt;  assert(has_error == true);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function testAssertEquals()&lt;br /&gt; {&lt;br /&gt;  assertEquals(1, 1);&lt;br /&gt;  local has_error = !mypcall(assertEquals, 1, 2);&lt;br /&gt;  assert(has_error == true);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function testAssertClose()&lt;br /&gt; {&lt;br /&gt;  assertClose(1, 1);&lt;br /&gt;  assertClose(-1, -1);&lt;br /&gt;  assertClose(0.1, -0.1, 0.2);&lt;br /&gt;  local has_error = !mypcall(assertEquals, 0.1, -0.1, 0.19);&lt;br /&gt;  assert(has_error == true);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function XtestXpcall()&lt;br /&gt; {&lt;br /&gt;  local f = function() {&lt;br /&gt;   error("[this is a normal error]");&lt;br /&gt;  }&lt;br /&gt;  local g = function(f) {&lt;br /&gt;   f();&lt;br /&gt;  }&lt;br /&gt;  g(f);&lt;br /&gt; }&lt;br /&gt;} // TestSqUnit&lt;br /&gt;&lt;br /&gt;//SqUnit().run("TestSqUnit.testAssertEquals"); // Will execute only one test&lt;br /&gt;//SqUnit().run("TestSqUnit"); // Will execute only one class of test&lt;br /&gt;SqUnit().run(); // Will execute all tests&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5321829307208613277?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5321829307208613277/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/05/squirrel.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5321829307208613277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5321829307208613277'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/05/squirrel.html' title='Squirrel 單元測試框架'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5082575446053496905</id><published>2009-05-23T13:02:00.002+08:00</published><updated>2009-05-23T14:36:35.460+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='SIMD'/><title type='text'>串列矩陣乘法優化</title><content type='html'>埋頭苦幹於 Squirrel 與其捆綁庫已有一個月多，偶爾間找一些好玩的來調劑一下，那就是有關 SIMD 的優化了。先呈上源碼：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;// simdMatrixMul.h&lt;br /&gt;#include &amp;lt;xmmintrin.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#if USE_ALIGNED&lt;br /&gt;# define _MM_LOAD _mm_load_ps&lt;br /&gt;# define _MM_STORE _mm_store_ps&lt;br /&gt;# define ASSERT_ALIGNED(p) assert(intptr_t(p) % 16 == 0)&lt;br /&gt;#else&lt;br /&gt;# define _MM_LOAD _mm_loadu_ps&lt;br /&gt;# define _MM_STORE _mm_storeu_ps&lt;br /&gt;# define ASSERT_ALIGNED(p)&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;/*! SIMD chained matrix multiplication&lt;br /&gt; Aliasing is allowed for the parameters&lt;br /&gt; \param result Pointer to 16 floats as the return value&lt;br /&gt; \param mats An array of pointer that point to a set of 16 floats&lt;br /&gt; \param count Number of pointer in mats&lt;br /&gt;&lt;br /&gt; Example:&lt;br /&gt; \code&lt;br /&gt; Matrix44 m1, m2, m3;&lt;br /&gt; Matrix44 result;&lt;br /&gt;&lt;br /&gt; // Perform result = m1 * m2 * m3;&lt;br /&gt; float* mats[] = { (flaot*)(&amp;amp;m1), (float*)(&amp;amp;m2), (float*)(&amp;amp;m3) };&lt;br /&gt; simdChainedMatrixMul((float*)&amp;amp;result, mats, 3);&lt;br /&gt; \endcode&lt;br /&gt;*/&lt;br /&gt;void simdChainedMatrixMul(float* result, const float* mats[], size_t count)&lt;br /&gt;{&lt;br /&gt; assert(count &amp;gt;= 2);&lt;br /&gt; ASSERT_ALIGNED(result);&lt;br /&gt;&lt;br /&gt; __m128 ret[4];&lt;br /&gt; __m128 x0, x1, x2, x3;&lt;br /&gt;&lt;br /&gt; // Load the first matrix into ret&lt;br /&gt; ret[0] = _MM_LOAD(mats[0] + 0*4);&lt;br /&gt; ret[1] = _MM_LOAD(mats[0] + 1*4);&lt;br /&gt; ret[2] = _MM_LOAD(mats[0] + 2*4);&lt;br /&gt; ret[3] = _MM_LOAD(mats[0] + 3*4);&lt;br /&gt;&lt;br /&gt; for(size_t j=1; j&amp;lt;count; ++j) {&lt;br /&gt;  ASSERT_ALIGNED(mats[j]);&lt;br /&gt;&lt;br /&gt;  // Prefetch the next matrix, may not usefull at all, test it case by case.&lt;br /&gt;  _mm_prefetch(reinterpret_cast&amp;lt;const char*&amp;gt;(&amp;amp;mats[j+1]), _MM_HINT_NTA);&lt;br /&gt;&lt;br /&gt;  __m128 x4 = _MM_LOAD(mats[j] + 0*4);&lt;br /&gt;  __m128 x5 = _MM_LOAD(mats[j] + 1*4);&lt;br /&gt;  __m128 x6 = _MM_LOAD(mats[j] + 2*4);&lt;br /&gt;  __m128 x7 = _MM_LOAD(mats[j] + 3*4);&lt;br /&gt;&lt;br /&gt;  for(size_t i=0; i&amp;lt;4; ++i) {&lt;br /&gt;   x1 = x2 = x3 = x0 = ret[i];&lt;br /&gt;   x0 = _mm_shuffle_ps(x0, x0, _MM_SHUFFLE(0,0,0,0));&lt;br /&gt;   x1 = _mm_shuffle_ps(x1, x1, _MM_SHUFFLE(1,1,1,1));&lt;br /&gt;   x2 = _mm_shuffle_ps(x2, x2, _MM_SHUFFLE(2,2,2,2));&lt;br /&gt;   x3 = _mm_shuffle_ps(x3, x3, _MM_SHUFFLE(3,3,3,3));&lt;br /&gt;&lt;br /&gt;   x0 = _mm_mul_ps(x0, x4);&lt;br /&gt;   x1 = _mm_mul_ps(x1, x5);&lt;br /&gt;   x2 = _mm_mul_ps(x2, x6);&lt;br /&gt;   x3 = _mm_mul_ps(x3, x7);&lt;br /&gt;&lt;br /&gt;   x2 = _mm_add_ps(x2, x0);&lt;br /&gt;   x3 = _mm_add_ps(x3, x1);&lt;br /&gt;   x3 = _mm_add_ps(x3, x2);&lt;br /&gt;&lt;br /&gt;   ret[i] = x3;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; _MM_STORE(result + 0*4, ret[0]);&lt;br /&gt; _MM_STORE(result + 1*4, ret[1]);&lt;br /&gt; _MM_STORE(result + 2*4, ret[2]);&lt;br /&gt; _MM_STORE(result + 3*4, ret[3]);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;這一函數專門計算串列矩陣乘法，非常適合用於 transform traversal 或 skeleton animation 等等。因為所有相乘後的臨時結果都存儲在 SSE 寄存器內，沒有多餘的記憶體移動指令被浪費丟。兩項優化的結果使得 simdChainedMatrixMul 比一般矩陣乘法快三倍，以下是一個 Entity traversal 的測試程式：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#include &amp;lt;assert.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;#include &amp;lt;time.h&amp;gt;&lt;br /&gt;&lt;br /&gt;// Benchmarking options&lt;br /&gt;#define USE_ALIGNED 1&lt;br /&gt;#define USE_SIMD 1&lt;br /&gt;&lt;br /&gt;#include "simdChainedMatrixMul.h"&lt;br /&gt;&lt;br /&gt;_MM_ALIGN16 class Matrix44&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt; void randomize()&lt;br /&gt; {&lt;br /&gt;  for(int i=0; i&amp;lt;4; ++i) for(int j=0; j&amp;lt;4; ++j)&lt;br /&gt;   data[i][j] = 0.96f * ((float(rand()) / RAND_MAX) * 2 - 1);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; float* operator[](size_t i) { return data[i]; }&lt;br /&gt; const float* operator[](size_t i) const { return data[i]; }&lt;br /&gt;&lt;br /&gt; // No aliasing is allowed for the 2 parameters&lt;br /&gt; void mul(Matrix44* __restrict result, const Matrix44* __restrict rhs)&lt;br /&gt; {&lt;br /&gt;  assert(result != rhs);&lt;br /&gt;  assert(result != this);&lt;br /&gt;&lt;br /&gt;/*  for(size_t i=0; i&amp;lt;4; ++i) for(size_t j=0; j&amp;lt;4; ++j) {&lt;br /&gt;   float sum = 0;&lt;br /&gt;   for(size_t k=0; k&amp;lt;4; ++k)&lt;br /&gt;    sum += data[i][k] * rhs-&amp;gt;data[k][j];&lt;br /&gt;   result-&amp;gt;data[i][j] = sum;&lt;br /&gt;  }&lt;br /&gt;  return;*/&lt;br /&gt;&lt;br /&gt;  result-&amp;gt;m00 = m00 * rhs-&amp;gt;m00 + m01 * rhs-&amp;gt;m10 + m02 * rhs-&amp;gt;m20 + m03 * rhs-&amp;gt;m30;&lt;br /&gt;  result-&amp;gt;m01 = m00 * rhs-&amp;gt;m01 + m01 * rhs-&amp;gt;m11 + m02 * rhs-&amp;gt;m21 + m03 * rhs-&amp;gt;m31;&lt;br /&gt;  result-&amp;gt;m02 = m00 * rhs-&amp;gt;m02 + m01 * rhs-&amp;gt;m12 + m02 * rhs-&amp;gt;m22 + m03 * rhs-&amp;gt;m32;&lt;br /&gt;  result-&amp;gt;m03 = m00 * rhs-&amp;gt;m03 + m01 * rhs-&amp;gt;m13 + m02 * rhs-&amp;gt;m23 + m03 * rhs-&amp;gt;m33;&lt;br /&gt;&lt;br /&gt;  result-&amp;gt;m10 = m10 * rhs-&amp;gt;m00 + m11 * rhs-&amp;gt;m10 + m12 * rhs-&amp;gt;m20 + m13 * rhs-&amp;gt;m30;&lt;br /&gt;  result-&amp;gt;m11 = m10 * rhs-&amp;gt;m01 + m11 * rhs-&amp;gt;m11 + m12 * rhs-&amp;gt;m21 + m13 * rhs-&amp;gt;m31;&lt;br /&gt;  result-&amp;gt;m12 = m10 * rhs-&amp;gt;m02 + m11 * rhs-&amp;gt;m12 + m12 * rhs-&amp;gt;m22 + m13 * rhs-&amp;gt;m32;&lt;br /&gt;  result-&amp;gt;m13 = m10 * rhs-&amp;gt;m03 + m11 * rhs-&amp;gt;m13 + m12 * rhs-&amp;gt;m23 + m13 * rhs-&amp;gt;m33;&lt;br /&gt;&lt;br /&gt;  result-&amp;gt;m20 = m20 * rhs-&amp;gt;m00 + m21 * rhs-&amp;gt;m10 + m22 * rhs-&amp;gt;m20 + m23 * rhs-&amp;gt;m30;&lt;br /&gt;  result-&amp;gt;m21 = m20 * rhs-&amp;gt;m01 + m21 * rhs-&amp;gt;m11 + m22 * rhs-&amp;gt;m21 + m23 * rhs-&amp;gt;m31;&lt;br /&gt;  result-&amp;gt;m22 = m20 * rhs-&amp;gt;m02 + m21 * rhs-&amp;gt;m12 + m22 * rhs-&amp;gt;m22 + m23 * rhs-&amp;gt;m32;&lt;br /&gt;  result-&amp;gt;m23 = m20 * rhs-&amp;gt;m03 + m21 * rhs-&amp;gt;m13 + m22 * rhs-&amp;gt;m23 + m23 * rhs-&amp;gt;m33;&lt;br /&gt;&lt;br /&gt;  result-&amp;gt;m30 = m30 * rhs-&amp;gt;m00 + m31 * rhs-&amp;gt;m10 + m32 * rhs-&amp;gt;m20 + m33 * rhs-&amp;gt;m30;&lt;br /&gt;  result-&amp;gt;m31 = m30 * rhs-&amp;gt;m01 + m31 * rhs-&amp;gt;m11 + m32 * rhs-&amp;gt;m21 + m33 * rhs-&amp;gt;m31;&lt;br /&gt;  result-&amp;gt;m32 = m30 * rhs-&amp;gt;m02 + m31 * rhs-&amp;gt;m12 + m32 * rhs-&amp;gt;m22 + m33 * rhs-&amp;gt;m32;&lt;br /&gt;  result-&amp;gt;m33 = m30 * rhs-&amp;gt;m03 + m31 * rhs-&amp;gt;m13 + m32 * rhs-&amp;gt;m23 + m33 * rhs-&amp;gt;m33;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; union {&lt;br /&gt;  // Individual elements&lt;br /&gt;  struct { float&lt;br /&gt;   m00, m01, m02, m03,&lt;br /&gt;   m10, m11, m12, m13,&lt;br /&gt;   m20, m21, m22, m23,&lt;br /&gt;   m30, m31, m32, m33;&lt;br /&gt;  };&lt;br /&gt;  // As a 2 dimension array&lt;br /&gt;  float data[4][4];&lt;br /&gt; };&lt;br /&gt;}; // Matrix44&lt;br /&gt;&lt;br /&gt;//! A simple Entity with single list structure.&lt;br /&gt;class Entity&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;#if USE_ALIGNED&lt;br /&gt; void* operator new(size_t size) { return _aligned_malloc(size, 16); }&lt;br /&gt; void operator delete(void* p) { _aligned_free(p); }&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt; Entity() : parent(NULL), child(NULL)&lt;br /&gt; {&lt;br /&gt;  ASSERT_ALIGNED(&amp;amp;matrix);&lt;br /&gt;  matrix.randomize();&lt;br /&gt; }&lt;br /&gt; ~Entity() { delete child; }&lt;br /&gt;&lt;br /&gt; Entity* addChild(Entity* e)&lt;br /&gt; {&lt;br /&gt;  assert(e-&amp;gt;child == NULL);&lt;br /&gt;  assert(!child);&lt;br /&gt;  if(!child) {&lt;br /&gt;   this-&amp;gt;child = e;&lt;br /&gt;   e-&amp;gt;parent = this;&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   e-&amp;gt;child = child;&lt;br /&gt;   child-&amp;gt;parent = e;&lt;br /&gt;   this-&amp;gt;child = e;&lt;br /&gt;   e-&amp;gt;parent = this;&lt;br /&gt;  }&lt;br /&gt;  return e;&lt;br /&gt; };&lt;br /&gt;&lt;br /&gt; Matrix44 calculateWorldMatrix1()&lt;br /&gt; {&lt;br /&gt;  Matrix44 result = matrix;&lt;br /&gt;  Entity* e = this;&lt;br /&gt;  while((e = e-&amp;gt;parent) != NULL) {&lt;br /&gt;   Matrix44 tmp(result);&lt;br /&gt;   tmp.mul(&amp;amp;result, &amp;amp;e-&amp;gt;matrix);&lt;br /&gt;  }&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; Matrix44 calculateWorldMatrix2()&lt;br /&gt; {&lt;br /&gt;  const float* matrixArray[1024] = {0};&lt;br /&gt;  size_t count = 0;&lt;br /&gt;  Entity* e = this;&lt;br /&gt;  do {&lt;br /&gt;   matrixArray[count++] = (float*)(&amp;amp;(e-&amp;gt;matrix));&lt;br /&gt;   if(count &amp;gt;= 1024)&lt;br /&gt;    exit(-1);&lt;br /&gt;  } while((e = e-&amp;gt;parent) != NULL);&lt;br /&gt;&lt;br /&gt;  Matrix44 result;&lt;br /&gt;  simdChainedMatrixMul((float*)(&amp;amp;result), matrixArray, count);&lt;br /&gt;&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; Matrix44 matrix;&lt;br /&gt; Entity* parent, *child;&lt;br /&gt;// char padding[1024*1024]; // To test the effect of cache miss&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int main(int argc, char* argv[])&lt;br /&gt;{&lt;br /&gt; srand(123);&lt;br /&gt; Entity* root = new Entity();&lt;br /&gt;&lt;br /&gt; Entity* lastChild = root;&lt;br /&gt; for(size_t i=1000; i--;)&lt;br /&gt;  lastChild = lastChild-&amp;gt;addChild(new Entity());&lt;br /&gt;&lt;br /&gt; for(size_t i=0; i&amp;lt;10; ++i) {&lt;br /&gt;  // Print out a summation variable to prevent compiler optimization&lt;br /&gt;  // that turns the whole benchmark into nothing&lt;br /&gt;  float sum = 0;&lt;br /&gt;&lt;br /&gt;  clock_t t1 = clock();&lt;br /&gt;&lt;br /&gt;  if(!USE_SIMD) for(size_t i=10000; i--;)&lt;br /&gt;   sum += lastChild-&amp;gt;calculateWorldMatrix1().m00;&lt;br /&gt;  else for(size_t i=10000; i--;)&lt;br /&gt;   sum += lastChild-&amp;gt;calculateWorldMatrix2().m00;&lt;br /&gt;&lt;br /&gt;  clock_t t2 = clock();&lt;br /&gt;&lt;br /&gt;  printf("%f, dummy:%i\n", float(t2 - t1) / CLOCKS_PER_SEC, sum);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; delete root;&lt;br /&gt; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5082575446053496905?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5082575446053496905/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/05/blog-post.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5082575446053496905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5082575446053496905'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/05/blog-post.html' title='串列矩陣乘法優化'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-6534801979017581399</id><published>2009-04-26T22:31:00.012+08:00</published><updated>2009-05-08T12:33:31.731+08:00</updated><title type='text'>明日之星 - 小松鼠 Squirrel</title><content type='html'>&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SfRyK99KgtI/AAAAAAAAAGI/FYtQbk_bGW8/s1600-h/army_squirrel.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 335px; height: 315px;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SfRyK99KgtI/AAAAAAAAAGI/FYtQbk_bGW8/s400/army_squirrel.jpg" alt="" id="BLOGGER_PHOTO_ID_5329009791841501906" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;（圖片來源：treehugger.com）&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;開始了把腳本語言加入遊戲引擎的工作，心目中的候選語言有 &lt;a href="http://www.lua.org/"&gt;Lua&lt;/a&gt;，&lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt;，&lt;a href="http://www.ruby-lang.org/"&gt;Ruby&lt;/a&gt;，&lt;a href="http://en.wikipedia.org/wiki/JavaScript"&gt;Javascript&lt;/a&gt; &lt;a href="http://code.google.com/p/v8/"&gt;V8&lt;/a&gt; 等等；最後選擇了這一隻小可愛 - &lt;a href="http://squirrel-lang.org/"&gt;Squirrel&lt;/a&gt;。小松鼠 Squirrel 由 &lt;a href="http://www.demichelis.net/default.aspx?content=resume&amp;amp;template=resume"&gt;Alberto Demichelis&lt;/a&gt; 所創立，他曾經是知名遊戲引擎公司 &lt;a href="http://www.crytek.com/"&gt;Crytek&lt;/a&gt; 的 System Architect，Far Cry/CRYEngine 裡的 Lua binding 庫都由他負責。就憑著他對 Lua 的透徹認識和深厚的編程功力，他決定打造一套比 Lua 更優秀的語言。&lt;br /&gt;&lt;br /&gt;一向有使用 Lua 的我非常欣賞它的堆疊設計與小巧的機器碼，可惜它的語法對我來說不太自然，甚至未有對類別和&lt;a href="http://zh.wikipedia.org/wiki/Unicode"&gt;標準字元編碼&lt;/a&gt;作直接支援。Squirrel 正好解決了這些已困擾我多時的問題，除此之外還加入了類似 C# 的 delegate 和 attribute。以下是 Squirrel 擁有的特色：&lt;br /&gt;&lt;ul&gt;&lt;li&gt;動態型別檢查 - Dynamic typing&lt;/li&gt;&lt;li&gt;委托模式 - Delegation&lt;/li&gt;&lt;li&gt;類別與繼承 - Classes &amp;amp; inheritance&lt;/li&gt;&lt;li&gt;類別属性 - Attribute&lt;/li&gt;&lt;li&gt;運算元重載 - Operator overloading&lt;br /&gt;&lt;/li&gt;&lt;li&gt;高階函數 - Higher order functions&lt;/li&gt;&lt;li&gt;迭代發生器 - Generators&lt;/li&gt;&lt;li&gt;協程 - Cooperative threads (coroutines)&lt;/li&gt;&lt;li&gt;最佳化遞歸 - Tail recursion&lt;/li&gt;&lt;li&gt;異常 - Exception handling&lt;/li&gt;&lt;li&gt;實時自動內儲管理 - Automatic memory management (CPU bursts free; mixed approach ref counting/GC)&lt;/li&gt;&lt;li&gt;弱引用 - Weak references&lt;/li&gt;&lt;li&gt;C/C++ 風格的語法&lt;/li&gt;&lt;li&gt;可支援 16 bit &lt;span class="definition"&gt;字串&lt;/span&gt;&lt;/li&gt;&lt;li&gt;支援&lt;a href="http://squirrel-lang.org/forums/thread/2974.aspx"&gt;運行期即時編譯 &lt;/a&gt;- Just in time compilation&lt;br /&gt;&lt;/li&gt;&lt;li&gt;支援 32 位和 64 位架構&lt;/li&gt;&lt;li&gt;整個運行庫不用七千行源碼，200k byte 的&lt;span class="definition"&gt;機械&lt;/span&gt;&lt;span class="definition"&gt;碼&lt;/span&gt;&lt;/li&gt;&lt;li&gt;遠程調試 - Remote debugging though TCP/IP&lt;/li&gt;&lt;li&gt;與 Visual Studio 的&lt;a href="http://squirrel-lang.org/forums/thread/3092.aspx"&gt;整合&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4CNEj03jaok/SgAyLRdJp2I/AAAAAAAAAGQ/aVd3P4sAIHw/s1600-h/SquirrelStudioIntegrated.PNG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 276px;" src="http://1.bp.blogspot.com/_4CNEj03jaok/SgAyLRdJp2I/AAAAAAAAAGQ/aVd3P4sAIHw/s400/SquirrelStudioIntegrated.PNG" alt="" id="BLOGGER_PHOTO_ID_5332317128052287330" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;在技術層面來看，Squirrel 絕對適合用作遊戲腳本語言，現在還欠缺的是一群使用者。小弟也希望它能夠茁壯成長，正在為它開發一套模板式的 C++ 結合庫，下次再和大家分享。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-6534801979017581399?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/6534801979017581399/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/04/squirrel.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6534801979017581399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6534801979017581399'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/04/squirrel.html' title='明日之星 - 小松鼠 Squirrel'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/SfRyK99KgtI/AAAAAAAAAGI/FYtQbk_bGW8/s72-c/army_squirrel.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-7739206286580982918</id><published>2009-03-31T15:43:00.010+08:00</published><updated>2009-04-01T23:32:20.006+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='tips'/><title type='text'>簡單地閱讀整個文件</title><content type='html'>有時你會想閱讀整個文件，而不是一行行或一個固定大小的緩衝區。這裡有一種方法來完成該工作：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;std::ifstream file("myFile", std::ios_base::binary);&lt;br /&gt;if(file) {&lt;br /&gt; std::ostringstream buffer;&lt;br /&gt; buffer &amp;lt;&amp;lt; file.rdbuf();&lt;br /&gt; file.close();&lt;br /&gt;&lt;br /&gt; std::string data = buffer.str();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;就這麼簡單。該文件的內容會&lt;span style="font-weight: bold;"&gt;複製&lt;/span&gt;到 ostringstream 去。&lt;br /&gt;相同的代碼可以用來複製文件，您只需要把 ostringstream 更換成一個 ofstream。&lt;br /&gt;請注意，以上代碼旨在簡潔，未必是高效的實作。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-7739206286580982918?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/7739206286580982918/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/03/blog-post.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7739206286580982918'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7739206286580982918'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/03/blog-post.html' title='簡單地閱讀整個文件'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-3471633115783978779</id><published>2009-03-19T09:54:00.004+08:00</published><updated>2009-03-19T10:19:10.854+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='visual studio'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><title type='text'>Visual Studio 插件推介 - RockScroll</title><content type='html'>這個插件把原本平平無奇的卷軸棒躍身一變成為整個文件的預覽。&lt;br /&gt;&lt;br /&gt;還有，當你雙擊選擇一個字，所有出現過的地方將自動突顯出來。&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/ScGn8EbPLWI/AAAAAAAAAGA/QLDyNKUBXbM/s1600-h/RockScroll.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: none; cursor: pointer; width: 400px; height: 325px;" src="http://3.bp.blogspot.com/_4CNEj03jaok/ScGn8EbPLWI/AAAAAAAAAGA/QLDyNKUBXbM/s400/RockScroll.png" alt="" id="BLOGGER_PHOTO_ID_5314713685695868258" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;現在就下載 &lt;a href="http://www.hanselman.com/blog/IntroducingRockScroll.aspx"&gt;RockScroll&lt;/a&gt; 並安裝它！&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-3471633115783978779?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/3471633115783978779/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/03/visual-studio-rockscroll.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3471633115783978779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3471633115783978779'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/03/visual-studio-rockscroll.html' title='Visual Studio 插件推介 - RockScroll'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_4CNEj03jaok/ScGn8EbPLWI/AAAAAAAAAGA/QLDyNKUBXbM/s72-c/RockScroll.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-8846696091256973274</id><published>2009-03-06T09:22:00.028+08:00</published><updated>2009-03-18T10:16:12.805+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='reprojection cache'/><category scheme='http://www.blogger.com/atom/ns#' term='ssao'/><category scheme='http://www.blogger.com/atom/ns#' term='glsl'/><category scheme='http://www.blogger.com/atom/ns#' term='assao'/><category scheme='http://www.blogger.com/atom/ns#' term='demo'/><title type='text'>Accumulative Screen Space Ambient Occlusion</title><content type='html'>This is my attempt to combine &lt;a href="http://www.cs.umbc.edu/%7Eolano/s2006c03/ch06.pdf"&gt;Real-Time Reprojection Cache&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Screen_Space_Ambient_Occlusion"&gt;Screen Space Ambient Occlusion&lt;/a&gt;. Using such caching scheme, the spatio-temporal coherence nature of the SSAO algorithm can be exploited. You can download the demo with shader source &lt;a href="http://rapidshare.com/files/208178129/ASSAO.7z"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;object height="405" width="500"&gt;&lt;param name="movie" value="http://www.youtube.com/v/MFkF4PcOAug&amp;amp;hl=zh_TW&amp;amp;fs=1&amp;amp;border=1"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/MFkF4PcOAug&amp;amp;hl=zh_TW&amp;amp;fs=1&amp;amp;border=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="405" width="500"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;span style="display: block;" id="formatbar_Buttons"&gt;&lt;span class="on" style="display: block;" id="formatbar_Add_Video" title="Add Video" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="addVideo();" onmousedown="CheckFormatting(event);;ButtonMouseDown(this);"&gt;&lt;img src="http://www.blogger.com/img/blank.gif" alt="Add Video" class="gl_video" border="0" /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;The name "Accumulative SSAO" comes from the fact that the occlusion value is accumulated and averaged over a number of frames. The algorithm itself is quite independent of how the occlusion is calculated and here I will assume the reader is familiar with SSAO implementation such as those from &lt;a href="http://delivery.acm.org/10.1145/1290000/1281671/p97-mittring.pdf?key1=1281671&amp;amp;key2=9942678811&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=15151515&amp;amp;CFTOKEN=6184618"&gt;Crysis&lt;/a&gt; and &lt;a href="http://www.google.com.hk/url?sa=t&amp;amp;source=web&amp;amp;ct=res&amp;amp;cd=1&amp;amp;url=http%3A%2F%2Fati.amd.com%2Fdeveloper%2Fsiggraph08%2Fchapter05-filion-starcraftii.pdf&amp;amp;ei=poeySc3rEZzm6QOxoei5BQ&amp;amp;usg=AFQjCNEPjX4aykapMxitSh1IDL6huN6XOg&amp;amp;sig2=7G37PNn1KF3H1zPpJdzrGg"&gt;Startcraft II&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;The pipeline&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For every frame,&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SbKLxImhxvI/AAAAAAAAAFw/eIbIKm7aQFM/s1600-h/ASSAO+pipeline.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 358px; height: 400px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SbKLxImhxvI/AAAAAAAAAFw/eIbIKm7aQFM/s400/ASSAO+pipeline.png" alt="" id="BLOGGER_PHOTO_ID_5310460586862167794" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SbKLxImhxvI/AAAAAAAAAFw/eIbIKm7aQFM/s400/ASSAO+pipeline.png"&gt;&lt;br /&gt;&lt;/a&gt;&lt;ol&gt;&lt;li&gt;The scene was rendered using deferred shading technique, producing the color, normal and depth buffers.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A number of random vectors were generated in CPU (where in usual SSAO these vectors only generated once in the program).&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The normal and depth buffer are then utilized to calculate the occlusion value in the SSAO pass.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Instead of writing the occlusion value to the final output, it would combine with the previous frame's accumulated occlusion value and then written to a second accumulation buffer.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A blur pass &lt;span style="color: rgb(0, 0, 0);"&gt;can optionally apply to&lt;/span&gt; the most updated accumulated occlusion buffer.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The color buffer was then combined with the occlusion value to product the final result, &lt;span style="color: rgb(0, 0, 0);"&gt;also &lt;/span&gt;the two accumulation buffers were switched with each other.&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Re-projection&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The re-projection happens in the SSAO pass when it tries to access the previous frame's occlusion value. Having the eye-space 3d position for each pixel, we can transform that into a texture coordinate by using a matrix (and a perspective division afterward), lets call it the delta matrix. This matrix is calculated on CPU as:&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;bias = translation(0.5, 0.5, 0.5) * scale(0.5, 0.5, 0.5)&lt;br /&gt;deltaMatrix = bias * lastFrameProjection * lastFrameView * currentFrameView.inverse()&lt;/pre&gt;&lt;br /&gt;In simple words, for each current frame's pixel, we are trying to locate their corresponding pixel coordinate &lt;span style="color: rgb(0, 0, 0);"&gt;on &lt;/span&gt;the last frame. If there is no camera movements, the two coordinates should be the same.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Accumulative AO&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;With the re-projection working, the current frame's occlusion value can be combined with the previous one with the following accumulation formula:&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;currentAo = currentAo / 30.0 + lastFrameAo * 29.0 / 30.0;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In order to make something interesting for the above equation, the current occlusion value should not be the same as the previous one. Therefore, a new set of sampling position should be generated for each frame, this can be done by re-generating the random unit sphere samples or the dithering texture every frame. In a loosely sense, it is doing a &lt;a href="http://en.wikipedia.org/wiki/Monte_Carlo_integration"&gt;Monte Carlo Integration&lt;/a&gt; over the time domain. To achieve better visual quality, more frames should be taken over the time.&lt;br /&gt;&lt;br /&gt;As each frame's AO value will also depends on the last few frames, there will be some time delay for the AO to become up-to-date in a dynamic scene. However, by changing the numerator and denominator in the equation, the trade-off between quality and responsiveness can be adjusted.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Cache-miss consideration&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Up to now the cache miss problem of the re-projection is not yet addressed. A cache miss will happen if somewhere in the scene that cannot be seen before becoming visible now, due to camera or object movement. Such a cache miss can be detected by comparing the current pixel's depth value with it's re-projected counterpart. If the two values differed by a certain threshold, a cache miss is detected. And to do this, the last frame's depth value is needed. Instead of using a separated texture to store the last frame's depth value, the depth can be encoded  and stored together with the accumulative AO value into a 32-bit texture.&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;// Encode a float value into 3 bytes&lt;br /&gt;// The input value should be in the range of [0, 1&lt;br /&gt;// Reference: http://www.ozone3d.net/blogs/lab/?p=113&lt;br /&gt;vec3 packFloatToVec3i(const float value)&lt;br /&gt;{&lt;br /&gt; const vec3 bitSh = vec3(256.0 * 256.0, 256.0, 1.0);&lt;br /&gt; const vec3 bitMsk = vec3(0.0, 1.0/256.0, 1.0/256.0);&lt;br /&gt; vec3 res = fract(value * bitSh);&lt;br /&gt; res -= res.xxy * bitMsk;&lt;br /&gt; return res;&lt;br /&gt;}&lt;br /&gt;float unpackFloatFromVec3i(const vec3 value)&lt;br /&gt;{&lt;br /&gt; const vec3 bitSh = vec3(1.0/(256.0*256.0), 1.0/256.0, 1.0);&lt;br /&gt; return dot(value, bitSh);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If there was a cached miss, the accumulative AO will be discarded and the instance AO value is used instead. Of course more samples can be taken in this frame to reduce the visual impact of the cache miss.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Discussion/improvements&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Currently a new independent set of random samples were generated for the above video demo. Other random sample over time generation method may reduce the noise.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;As some of the re-projection cache scheme suggested, a cache value should be cleared after a certain period of time to avoid in-stability and provide a better response to dynamic environment, and this is done here by the accumulation formula.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;To reduce cache miss due to object movement, each object's last transformation matrix can also be incorporated into the algorithm.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The depth encoding scheme also make the blur pass much more efficient.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The explained algorithm provides a new way to improve the quality and efficiency of traditional SSAO by using the result from a number of frames instead of one. It also opens up more parameters and sampling patterns to explore with.&lt;a href="http://mtlung.googlepages.com/ASSAO.7z"&gt;&lt;br /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-8846696091256973274?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/8846696091256973274/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/03/accumulative-screen-space-ambient.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/8846696091256973274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/8846696091256973274'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/03/accumulative-screen-space-ambient.html' title='Accumulative Screen Space Ambient Occlusion'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_4CNEj03jaok/SbKLxImhxvI/AAAAAAAAAFw/eIbIKm7aQFM/s72-c/ASSAO+pipeline.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-4692921709771169742</id><published>2009-02-17T16:25:00.006+08:00</published><updated>2009-02-23T18:56:16.177+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='library'/><title type='text'>用 C# 製作使用者界面</title><content type='html'>過去兩個星期開始製作使用者界面，選擇了 C# 加 Microsoft Windows Form 作為平台。&lt;br /&gt;除了 C# 本身容易使用外，最令我覺得欣慰的就是那龐大的使用者社區，好一些想要的功能都已經有其他人做好了，或已有詳盡的教學可供參考。&lt;br /&gt;以下是一些在我製作 Studio Tool 的時候所用到的一些 library:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://sourceforge.net/projects/dockpanelsuite/"&gt;Dock Panel Suite&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.dreamincode.net/code/snippet2591.htm"&gt;Directory Treeview&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://sourceforge.net/projects/mstreeview/"&gt;Multiple Selectable Treeview&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.codeproject.com/KB/miscctrl/bending_property.aspx"&gt;PropertyGrid without property&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4CNEj03jaok/SZqJGWPHOxI/AAAAAAAAAFg/nMvBmL0aT7Y/s1600-h/Studio.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 300px;" src="http://1.bp.blogspot.com/_4CNEj03jaok/SZqJGWPHOxI/AAAAAAAAAFg/nMvBmL0aT7Y/s400/Studio.png" alt="" id="BLOGGER_PHOTO_ID_5303702253323369234" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-4692921709771169742?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/4692921709771169742/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/02/c.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/4692921709771169742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/4692921709771169742'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/02/c.html' title='用 C# 製作使用者界面'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_4CNEj03jaok/SZqJGWPHOxI/AAAAAAAAAFg/nMvBmL0aT7Y/s72-c/Studio.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-2098690420559015305</id><published>2009-02-16T10:17:00.005+08:00</published><updated>2009-03-18T10:47:28.009+08:00</updated><title type='text'>移除 ".svn" 文件夾</title><content type='html'>話說有一天我想著怎樣把一個專案之下的 ".svn" 文件夾全部移除，想著想著的都是一些 Linux command。最後找到這個：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;find -type d -name .svn -exec rm -rf {} \;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span id="dropdownid"&gt;怎知&lt;/span&gt;原來每天使用的 &lt;a href="http://tortoisesvn.tigris.org/"&gt;Tortoise SVN&lt;/a&gt; 已經有這個功能... 又做了一件愚蠢的事 XD&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SZjOe3KMlEI/AAAAAAAAAFY/eU74Kc1ykh8/s1600-h/SVN_Export.PNG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 338px; height: 288px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SZjOe3KMlEI/AAAAAAAAAFY/eU74Kc1ykh8/s400/SVN_Export.PNG" alt="" id="BLOGGER_PHOTO_ID_5303215590826873922" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-2098690420559015305?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/2098690420559015305/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/02/svn.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2098690420559015305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2098690420559015305'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/02/svn.html' title='移除 &quot;.svn&quot; 文件夾'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_4CNEj03jaok/SZjOe3KMlEI/AAAAAAAAAFY/eU74Kc1ykh8/s72-c/SVN_Export.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-2155738448863080331</id><published>2009-02-04T14:25:00.006+08:00</published><updated>2009-02-04T22:33:47.020+08:00</updated><title type='text'>九型人格分析</title><content type='html'>看來我滿喜歡建造遊戲引擎來成全自己亦希望同時成就他人。&lt;br /&gt;&lt;br /&gt;&lt;div style="margin: 0pt auto; padding: 0pt 6px; background: rgb(255, 255, 255) none repeat scroll 0% 0%; width: 400px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;table style="border: 1px solid rgb(204, 204, 204); margin: 0pt auto; font-size: 9pt; width: 100%;"&gt;&lt;caption&gt;九型人格分析&lt;/caption&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(80, 163, 218) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第二型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Helpers, Givers, Caretakers"&gt;助人者、全愛型、助人型、成就他人者、博愛型&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 14%&lt;/span&gt;&lt;div style="background: rgb(80, 163, 218) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 43px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(0, 204, 0) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第一型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Reformers, Critics, Perfectionists"&gt;完美主義者、完美型、改革者、改進型、秩序大使&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 13%&lt;/span&gt;&lt;div style="background: rgb(0, 204, 0) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 40px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(205, 92, 92) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第六型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Loyalists, Devil's Advocates, Defenders"&gt;忠誠型、忠誠型、尋找安全者、謹慎型&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 13%&lt;/span&gt;&lt;div style="background: rgb(205, 92, 92) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 40px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(177, 119, 169) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第三型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Achievers, Performers, Succeeders"&gt;成就者、事業型、成就型、實踐型&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 12%&lt;/span&gt;&lt;div style="background: rgb(177, 119, 169) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 37px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(240, 128, 128) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第四型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Romantics, Individualists, Artists"&gt;藝術型、浪漫者、自我型、憑感覺者&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 11%&lt;/span&gt;&lt;div style="background: rgb(240, 128, 128) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 34px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(185, 178, 4) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第五型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Observers, Thinkers, Investigators"&gt;智慧型、觀察者、思想型、理性分析者、思考型&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 9%&lt;/span&gt;&lt;div style="background: rgb(185, 178, 4) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 28px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(70, 130, 180) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第七型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Enthusiasts, Adventurers, Sensationalists"&gt;快樂主義型、豐富型、活躍型、創造可能者、享樂型&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 9%&lt;/span&gt;&lt;div style="background: rgb(70, 130, 180) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 28px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(255, 99, 71) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第八型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Leaders, Protectors, Challengers"&gt;領袖型、能力型、挑戰者、保護者、權威型&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 9%&lt;/span&gt;&lt;div style="background: rgb(255, 99, 71) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 28px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding: 6px; background: rgb(20, 133, 113) none repeat scroll 0% 0%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;第九型&lt;/td&gt;&lt;td style="padding: 0pt; text-align: left; vertical-align: top; width: 300px;"&gt;&lt;span title="Mediators, Peacemakers, Preservationists"&gt;和平型、和平者、和諧型、維持和諧者&lt;/span&gt;&lt;div style="background: rgb(204, 204, 204) none repeat scroll 0% 0%; height: 1.5em; width: 300px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;span style="position: absolute;color:white;" &gt; 7%&lt;/span&gt;&lt;div style="background: rgb(20, 133, 113) none repeat scroll 0% 0%; color: white; height: 1.5em; width: 22px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div style="text-align: right;"&gt;&lt;a href="http://tiquiz.blogspot.com/2007/08/180.html" style="font-size: 9pt; text-decoration: none;"&gt;&lt;span style="color: rgb(255, 128, 0);"&gt;我&lt;/span&gt;&lt;span style="color: rgb(230, 153, 25);"&gt;的&lt;/span&gt;&lt;span style="color: rgb(205, 178, 50);"&gt;九&lt;/span&gt;&lt;span style="color: rgb(180, 192, 75);"&gt;型&lt;/span&gt;&lt;span style="color: rgb(155, 192, 100);"&gt;人&lt;/span&gt;&lt;span style="color: rgb(130, 192, 125);"&gt;格&lt;/span&gt;&lt;span style="color: rgb(105, 192, 150);"&gt;分&lt;/span&gt;&lt;span style="color: rgb(80, 192, 175);"&gt;析&lt;/span&gt;&lt;span style="color: rgb(55, 192, 200);"&gt;？&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font-size: 9pt;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-2155738448863080331?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/2155738448863080331/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/02/blog-post.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2155738448863080331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2155738448863080331'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/02/blog-post.html' title='九型人格分析'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5751932223703853427</id><published>2009-01-15T14:43:00.004+08:00</published><updated>2009-01-15T15:48:23.608+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shader'/><category scheme='http://www.blogger.com/atom/ns#' term='ssao'/><category scheme='http://www.blogger.com/atom/ns#' term='glsl'/><category scheme='http://www.blogger.com/atom/ns#' term='opengl'/><title type='text'>SSAO Demo</title><content type='html'>花了一點時間整理好我的 SSAO Demo，請按&lt;a href="http://mtlung.googlepages.com/SSAOTest.zip"&gt;這裡&lt;/a&gt;下載。請各位多給意見 ：)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SW7e8mpvYXI/AAAAAAAAAFQ/1oUBfhpjmtU/s1600-h/SSAODemo.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 311px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SW7e8mpvYXI/AAAAAAAAAFQ/1oUBfhpjmtU/s400/SSAODemo.png" alt="" id="BLOGGER_PHOTO_ID_5291411744956047730" border="0" /&gt;&lt;/a&gt;操縱方法：&lt;br /&gt;&lt;ul&gt;&lt;li&gt;鏡頭移動：W、A、S、D、Page up、Page down&lt;/li&gt;&lt;li&gt;鏡頭方向：滑鼠左鍵&lt;/li&gt;&lt;li&gt;SSAO開關：F1，默認值：開&lt;/li&gt;&lt;li&gt;銀幕減半開關：F2，默認值：開&lt;/li&gt;&lt;li&gt;增大/減少模糊操作次數：F3/Shift+F3，默認值：2&lt;/li&gt;&lt;li&gt;漫射材質開關：F4，默認值：關&lt;/li&gt;&lt;li&gt;增大/減少閉塞半徑：Shift+F5/F5&lt;/li&gt;&lt;/ul&gt;後記：&lt;br /&gt;&lt;ul&gt;&lt;li&gt;看過 &lt;a href="http://blog.nextrevision.com/?p=76"&gt;R5 Demo&lt;/a&gt; 後才覺悟增大 blur pass 次數和 blur kernel size 的分別。 Orz&lt;br /&gt;&lt;/li&gt;&lt;li&gt;把 &lt;a href="http://www.ozone3d.net/blogs/lab/?p=113"&gt;depth encode&lt;/a&gt; 到三個 8-bit integer，那麼一塊 32-bit 的 RGBA render target 就可以運載 occlusion value 和 depth 到模糊操作中，從而大大減少 texture fetch 的數量。&lt;/li&gt;&lt;/ul&gt;&lt;span style="color: rgb(51, 102, 255);font-size:85%;" &gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-size:100%;"&gt;相關文章&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://mtlung.blogspot.com/2008/12/ssao.html"&gt;新年前的 SSAO&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://mtlung.blogspot.com/2008/09/ssao.html"&gt;SSAO 新進展&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://mtlung.blogspot.com/2008/09/shader.html"&gt;初嚐 Shader 編程&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://ati.amd.com/developer/SIGGRAPH08/Chapter05-Filion-StarCraftII.pdf"&gt;Starcraft 2 - Effects &amp;amp; Techniques&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5751932223703853427?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5751932223703853427/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/01/ssao-demo.html#comment-form' title='4 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5751932223703853427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5751932223703853427'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/01/ssao-demo.html' title='SSAO Demo'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_4CNEj03jaok/SW7e8mpvYXI/AAAAAAAAAFQ/1oUBfhpjmtU/s72-c/SSAODemo.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-32989237868380095</id><published>2009-01-13T17:24:00.012+08:00</published><updated>2009-03-18T10:46:21.397+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code snip'/><title type='text'>檔案監視器</title><content type='html'>看過了&lt;a href="http://blog.monkeypotion.net/"&gt;猴子靈藥&lt;/a&gt;的 "&lt;a href="http://blog.monkeypotion.net/gameprog/advanced/database-hot-loader-introduction"&gt;Database Hot Loader&lt;/a&gt;" 後，心動之下又想做些類似的東西，哪就是一個用來監視檔案系統的小工具。有了它，遊戲裡的任何素材(美術素材，以及音效、字型、腳本程序等等)&lt;span style="font-weight: bold;"&gt;檔案一經修改就會立即在遊戲裡反映出來，因而省去重新啟動遊戲程式的煩厭&lt;/span&gt;。當然背後還需健全的&lt;a href="http://mtlung.blogspot.com/2008/06/resources-loading.html"&gt;資源系統&lt;/a&gt;才能成事。&lt;br /&gt;&lt;br /&gt;在視窗環境中，標準的方法是調用 &lt;span style="font-size:85%;"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/aa364417%28VS.85%29.aspx"&gt;FindFirstChangeNotification&lt;/a&gt;&lt;/span&gt; 或 &lt;span style="font-size:85%;"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/aa365465%28VS.85%29.aspx"&gt;ReadDirectoryChangesW&lt;/a&gt;&lt;/span&gt;；前者告訴你某個資料夾有否被更改，後者還會告訴你甚麼檔案/資料夾曾被更改。怎知 &lt;span style="font-size:85%;"&gt;ReadDirectoryChangesW&lt;/span&gt; 的調用殊不簡單，MSDN 又沒有範例，上 &lt;a href="http://www.codeproject.com/"&gt;CodeProject&lt;/a&gt; 碰碰運氣得來的是一個 3000 多行代碼的&lt;a href="http://www.codeproject.com/KB/files/directorychangewatcher.aspx"&gt;類別&lt;/a&gt;，Google 一翻還是找不到想要的。&lt;br /&gt;&lt;br /&gt;經過一翻努力和嘗試(我相信 MSDN 是有錯漏的)，&lt;span style="font-size:85%;"&gt;&lt;span style="font-size:100%;"&gt;得知&lt;/span&gt; GetOverlappedResult&lt;/span&gt; 和 &lt;span style="font-size:85%;"&gt;ReadDirectoryChangesW &lt;/span&gt;的配合是最簡單的；無須和任何多緒有關的東西打交道。我的 FileMonitor 就只有 constructor 和 getChangedFile 這兩個函數。&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 102, 102);"&gt;FileMonitor.h&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#ifndef __FILEMONITOR__&lt;br /&gt;#define __FILEMONITOR__&lt;br /&gt;&lt;br /&gt;#include &amp;lt;string&amp;gt;&lt;br /&gt;&lt;br /&gt;/*! To monitor file changes under a particular folder.&lt;br /&gt; The implementation use the win32 ReadDirectoryChangesW() function&lt;br /&gt; with GetOverlappedResult() to perform the monitoring, therefore&lt;br /&gt; no thread is created and so making the interface very simple.&lt;br /&gt;&lt;br /&gt; With the limitation of ReadDirectoryChangesW() is using a fixed buffer&lt;br /&gt; to hold the information between calls of getChangedFile(), FileMonitor&lt;br /&gt; may fail to detect file changes between calls of getChangedFile() if&lt;br /&gt; the file names are too large to fit into the buffer. To overcome the&lt;br /&gt; issue, you need to call getChangedFile() frequently.&lt;br /&gt;&lt;br /&gt; \sa http://mtlung.blogspot.com/2009/01/blog-post.html&lt;br /&gt;&lt;br /&gt; Example:&lt;br /&gt; \code&lt;br /&gt; FileMonitor monitor(L"pathToMonitor", true);&lt;br /&gt; // In your main loop:&lt;br /&gt; while(true) {&lt;br /&gt;  std::wstring path;&lt;br /&gt;  while(!(path = monitor.getChangedFile()).empty()) {&lt;br /&gt;   std::wcout &amp;lt;&amp;lt; path &amp;lt;&amp;lt; std::endl;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; \endcode&lt;br /&gt; */&lt;br /&gt;class FileMonitor&lt;br /&gt;{&lt;br /&gt; // FileMonitor is non-copyable&lt;br /&gt; FileMonitor(const FileMonitor&amp;amp;);&lt;br /&gt; FileMonitor&amp;amp; operator=(const FileMonitor&amp;amp;);&lt;br /&gt; &lt;br /&gt;public:&lt;br /&gt; /*! Constructor&lt;br /&gt;  \param path The path to monitor&lt;br /&gt;  \param recursive Watch the path recursively&lt;br /&gt;  \param operationTowatch Which file operation to monitor with,&lt;br /&gt;  having the same meaning as the dwNotifyFilter in ReadDirectoryChangesW()&lt;br /&gt;  function, see http://msdn.microsoft.com/en-us/library/aa365465(VS.85).aspx&lt;br /&gt;  for more details. By default, it's value is -1 and have the same meaning&lt;br /&gt;  as FILE_NOTIFY_CHANGE_LAST_WRITE&lt;br /&gt;  */&lt;br /&gt; FileMonitor(const wchar_t* path, bool recursive, int operationTowatch = -1);&lt;br /&gt;&lt;br /&gt; ~FileMonitor();&lt;br /&gt;&lt;br /&gt; /*! Get which file under the watching directory is changed.&lt;br /&gt;  This function is non-blocking and if there is no changes in the&lt;br /&gt;  file system, it will simple return an empty string.&lt;br /&gt;&lt;br /&gt;  \note&lt;br /&gt;  The current implementation use a fixed buffer to capture all the&lt;br /&gt;  file changes between calls of getChangedFile(). If there are too&lt;br /&gt;  much changes or the file names get too long, the buffer overflow&lt;br /&gt;  and that file change notification will lost.&lt;br /&gt;  See more on the documentation of ReadDirectoryChangesW() in MSDN.&lt;br /&gt;  */&lt;br /&gt; std::wstring getChangedFile() const;&lt;br /&gt;&lt;br /&gt; private:&lt;br /&gt; class Impl; //!&amp;lt; Private implementation class&lt;br /&gt; Impl* mImpl;&lt;br /&gt;}; // FileMonitor&lt;br /&gt;&lt;br /&gt;#endif // __FILEMONITOR__&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 102, 102);"&gt;FileMonitor.cpp&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#include "FileMonitor.h"&lt;br /&gt;#include &amp;lt;assert.h&amp;gt;&lt;br /&gt;#include &amp;lt;iostream&amp;gt;&lt;br /&gt;#include &amp;lt;list&amp;gt;&lt;br /&gt;&lt;br /&gt;// Exclude rarely-used stuff from Windows headers&lt;br /&gt;#ifndef WIN32_LEAN_AND_MEAN&lt;br /&gt;# define WIN32_LEAN_AND_MEAN&lt;br /&gt;#endif&lt;br /&gt;#ifndef VC_EXTRALEAN&lt;br /&gt;# define VC_EXTRALEAN&lt;br /&gt;#endif&lt;br /&gt;#include &amp;lt;windows.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#ifdef _MSC_VER // Currently only windows is supported&lt;br /&gt;&lt;br /&gt;class FileMonitor::Impl&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt; Impl(const wchar_t* path, bool recursive, int operationTowatch)&lt;br /&gt;  : mRecursive(recursive), mOperationTowatch(operationTowatch)&lt;br /&gt; {&lt;br /&gt;  assert(int(mBuffer) % 4 == 0 &amp;amp;&amp;amp; "Address of mBuffer must be 4-byte aligned");&lt;br /&gt;&lt;br /&gt;  // Adjust the defalt value for mOperationTowatch&lt;br /&gt;  if(mOperationTowatch == -1)&lt;br /&gt;   mOperationTowatch = FILE_NOTIFY_CHANGE_LAST_WRITE;&lt;br /&gt;&lt;br /&gt;  mDirectory = ::CreateFileW(&lt;br /&gt;   path,&lt;br /&gt;   FILE_LIST_DIRECTORY,&lt;br /&gt;   FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,&lt;br /&gt;   0,&lt;br /&gt;   OPEN_EXISTING,&lt;br /&gt;   // ReadDirectoryChangesW() needs FILE_FLAG_BACKUP_SEMANTICS&lt;br /&gt;   FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,&lt;br /&gt;   0&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;  memset(&amp;amp;mOverlapped, 0, sizeof(mOverlapped));&lt;br /&gt;  if(!mDirectory || !readChange()) {&lt;br /&gt;   ::CloseHandle(mDirectory);&lt;br /&gt;   mDirectory = NULL;&lt;br /&gt;   std::wcerr &amp;lt;&amp;lt; L"Fail to watch directory: " &amp;lt;&amp;lt; path &amp;lt;&amp;lt; std::endl;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; ~Impl()&lt;br /&gt; {&lt;br /&gt;  ::CloseHandle(mDirectory);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; bool readChange() const&lt;br /&gt; {&lt;br /&gt;  return ::ReadDirectoryChangesW(&lt;br /&gt;   mDirectory,&lt;br /&gt;   mBuffer, sizeof(mBuffer),&lt;br /&gt;   mRecursive,&lt;br /&gt;   mOperationTowatch,&lt;br /&gt;   NULL,  // bytesRetured&lt;br /&gt;   &amp;amp;mOverlapped,&lt;br /&gt;   0   // callBack&lt;br /&gt;  ) != 0;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; std::wstring getChangedFile() const&lt;br /&gt; {&lt;br /&gt;  // We will try to call GetOverlappedResult() even there are entries inside&lt;br /&gt;  // mFiles, so that it's less possible for the mBuffer to be overflowed.&lt;br /&gt;&lt;br /&gt;  // For some unknown reason(s) ReadDirectoryChangesW() will report the file twice,&lt;br /&gt;  // therefore we add a loop to filter out those duplicated entries.&lt;br /&gt;  for(size_t i=2; i--;)&lt;br /&gt;  {&lt;br /&gt;   DWORD bytesRetured = 0;&lt;br /&gt;   if(0 == ::GetOverlappedResult(mDirectory, &amp;amp;mOverlapped, &amp;amp;bytesRetured, false))&lt;br /&gt;    goto CACHED; // The use of goto here makes the code clean.&lt;br /&gt;&lt;br /&gt;   if(bytesRetured == 0) {&lt;br /&gt;    // TODO: To reduce the chance of insufficient buffer,&lt;br /&gt;    // we can move the code to another thread.&lt;br /&gt;    std::wcerr &amp;lt;&amp;lt; L"Error returned by ReadDirectoryChangesW(), "&lt;br /&gt;     L"most likely the internal buffer is too small" &amp;lt;&amp;lt; std::endl;&lt;br /&gt;    readChange();&lt;br /&gt;    goto CACHED;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   FILE_NOTIFY_INFORMATION* p = reinterpret_cast&amp;lt;FILE_NOTIFY_INFORMATION*&amp;gt;(mBuffer);&lt;br /&gt;   while(true)&lt;br /&gt;   {&lt;br /&gt;    std::wstring fileName(p-&amp;gt;FileName, p-&amp;gt;FileNameLength / sizeof(wchar_t));&lt;br /&gt;&lt;br /&gt;    // Skip duplicated entry&lt;br /&gt;    if(mFiles.empty() || fileName != mFiles.back())&lt;br /&gt;     mFiles.push_back(fileName);&lt;br /&gt;&lt;br /&gt;    if(p-&amp;gt;NextEntryOffset == 0)&lt;br /&gt;     break;&lt;br /&gt;&lt;br /&gt;    p = reinterpret_cast&amp;lt;FILE_NOTIFY_INFORMATION*&amp;gt;((char*)p + p-&amp;gt;NextEntryOffset);&lt;br /&gt;&lt;br /&gt;    // Do some extra buffer overflow check.&lt;br /&gt;    if((char*)p - (char*)mBuffer &amp;gt; sizeof(mBuffer))&lt;br /&gt;     break;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   if(!readChange())&lt;br /&gt;    return L"";&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  CACHED:&lt;br /&gt;  if(!mFiles.empty()) {&lt;br /&gt;   std::wstring ret = mFiles.front();&lt;br /&gt;   mFiles.pop_front();&lt;br /&gt;   return ret;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return L"";&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; HANDLE mDirectory;&lt;br /&gt; bool mRecursive;&lt;br /&gt; int mOperationTowatch;&lt;br /&gt; /*! This buffer must be 4-byte aligned, therefore we use int as the type.&lt;br /&gt; You may change the buffer size to fit your needs.&lt;br /&gt; */&lt;br /&gt; mutable int mBuffer[2048];&lt;br /&gt; mutable OVERLAPPED mOverlapped;&lt;br /&gt; //! A list of wstring acting as a circular buffer.&lt;br /&gt; mutable std::list&amp;lt;std::wstring&amp;gt; mFiles;&lt;br /&gt;}; // Impl&lt;br /&gt;&lt;br /&gt;FileMonitor::FileMonitor(const wchar_t* path, bool recursive, int operationTowatch)&lt;br /&gt;{&lt;br /&gt; mImpl = new Impl(path, recursive, operationTowatch);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;FileMonitor::~FileMonitor()&lt;br /&gt;{&lt;br /&gt; delete mImpl;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;std::wstring FileMonitor::getChangedFile() const&lt;br /&gt;{&lt;br /&gt; __assume(mImpl); // We know mImpl is always not null, shut off the C++ analysis warning&lt;br /&gt; return mImpl-&amp;gt;getChangedFile();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#endif // _MSC_VER&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 102, 102);"&gt;Main.cpp&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#include "FileMonitor.h"&lt;br /&gt;#include &amp;lt;iostream&amp;gt;&lt;br /&gt;#include &amp;lt;conio.h&amp;gt; // For _kbhit()&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt; FileMonitor monitor(L"./", true);&lt;br /&gt;&lt;br /&gt; std::wcout &amp;lt;&amp;lt; L"Create and modify the files in the current directory, "&lt;br /&gt; L"and the FileMonitor will tell you the name of those files.";&lt;br /&gt; std::wcout &amp;lt;&amp;lt; L" Press any key to quit the program" &amp;lt;&amp;lt; std::endl;&lt;br /&gt;&lt;br /&gt; while(!_kbhit())&lt;br /&gt; {&lt;br /&gt;  std::wstring path;&lt;br /&gt;  // Keep polling the monitor, But a real application should&lt;br /&gt;  // only poll the monitor once a while.&lt;br /&gt;  while(!(path = monitor.getChangedFile()).empty())&lt;br /&gt;  {&lt;br /&gt;  std::wcout &amp;lt;&amp;lt; path &amp;lt;&amp;lt; std::endl;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-32989237868380095?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/32989237868380095/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2009/01/blog-post.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/32989237868380095'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/32989237868380095'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2009/01/blog-post.html' title='檔案監視器'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-1685509982163803835</id><published>2008-12-31T22:55:00.000+08:00</published><updated>2008-12-31T22:55:21.528+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ssao'/><category scheme='http://www.blogger.com/atom/ns#' term='opengl'/><title type='text'>新年前的 SSAO</title><content type='html'>相信很多朋友都趁年尾來一篇日誌，我也來熱鬧一番 ^.^&lt;br /&gt;&lt;a href="http://mtlung.blogspot.com/2008/09/ssao.html"&gt;上一篇&lt;/a&gt;的 SSAO 日誌距今已有三個月；之後更改了演算法，本想多作改進和包裝成 demo 才放進來，但現在手頭上還正在建造一個給 OpenGl 的 Effect 架構，此刻只好放些 Screen shot 好了。&lt;br /&gt;&lt;br /&gt;演算法和 &lt;a href="http://mtlung.blogspot.com/2008/09/2.html"&gt;星海爭霸2&lt;/a&gt; 的&lt;span id="dropdownid"&gt;相&lt;/span&gt;&lt;span id="dropdownid"&gt;近，&lt;/span&gt;但我對 Image processing 的學識尚淺，&lt;a href="http://en.wikipedia.org/wiki/Bilateral_filter"&gt;Bilateral (edge-preserving) blur&lt;/a&gt; 的部分還有問題。&lt;br /&gt;&lt;br /&gt;最後祝大家&lt;span style="color: rgb(204, 102, 0); font-weight: bold;"&gt;新年快樂&lt;/span&gt;。&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SVt-e-6DvpI/AAAAAAAAAE4/ppQ-dxWUe6g/s1600-h/Bike1.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: none; cursor: pointer; width: 400px; height: 311px;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SVt-e-6DvpI/AAAAAAAAAE4/ppQ-dxWUe6g/s400/Bike1.png" alt="" id="BLOGGER_PHOTO_ID_5285957658397097618" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;沒有SSAO, 900 fps&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SVt-tm_iyII/AAAAAAAAAFA/Kug24wMNnuE/s1600-h/Bike2.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: none; cursor: pointer; width: 400px; height: 311px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SVt-tm_iyII/AAAAAAAAAFA/Kug24wMNnuE/s400/Bike2.png" alt="" id="BLOGGER_PHOTO_ID_5285957909675690114" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;屏幕 1/4 SSAO with dither, 252 fps&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SVt_Bs6ukGI/AAAAAAAAAFI/9gUeMTWr-Ag/s1600-h/Bike3.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: none; cursor: pointer; width: 400px; height: 311px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SVt_Bs6ukGI/AAAAAAAAAFI/9gUeMTWr-Ag/s400/Bike3.png" alt="" id="BLOGGER_PHOTO_ID_5285958254863487074" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;屏幕 1/4 SSAO with dither + blur, 233 fps&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-1685509982163803835?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/1685509982163803835/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/12/ssao.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/1685509982163803835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/1685509982163803835'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/12/ssao.html' title='新年前的 SSAO'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/SVt-e-6DvpI/AAAAAAAAAE4/ppQ-dxWUe6g/s72-c/Bike1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-8724394595279705003</id><published>2008-12-20T14:25:00.004+08:00</published><updated>2008-12-20T16:05:05.788+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shader'/><category scheme='http://www.blogger.com/atom/ns#' term='philosophy'/><title type='text'>Is graphical shader system really that good?</title><content type='html'>每當我們&lt;span id="dropdownid"&gt;看&lt;/span&gt;到什麼的遊戲引擎有著一個可視化的 Shader 系統後都會&lt;span id="dropdownid"&gt;覺得它分外&lt;/span&gt;強勁；&lt;br /&gt;它一般被認為可以令製作流程更順暢，但這概念是否每一處也適用哩？讓我們聽一聽反對的聲音吧。&lt;ul&gt;&lt;li&gt;&lt;a href="http://realtimecollisiondetection.net/blog/?p=73" rel="bookmark" title="Permanent link to Graphical shader systems are bad"&gt;Graphical shader systems are bad&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://c0de517e.blogspot.com/2008/08/commenting-on-graphical-shader-systems.html"&gt;Commenting on graphical shader systems&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://diaryofagraphicsprogrammer.blogspot.com/2008/09/shader-workflow-why-shader-generators.html"&gt;Shader Workflow - Why Shader Generators are Bad&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;綜合各人的要點：&lt;br /&gt;&lt;ul&gt;&lt;li&gt;把電腦語言可視化不是一種萬靈丹。&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;用於非實時的 Rendering 還不錯，但用於講求高效率的實時 Rendering 就會帶來問題。&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;經可視化工具產生的 Shader 一般都比手寫的來得低效。&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;太容易去讓美術師建造數以千計獨一無二互相無關的 Shader，使後期整理成為惡夢。&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;削弱美術師和程式設計師之間的溝通。&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-8724394595279705003?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/8724394595279705003/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/12/is-graphical-shader-system-really-that.html#comment-form' title='3 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/8724394595279705003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/8724394595279705003'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/12/is-graphical-shader-system-really-that.html' title='Is graphical shader system really that good?'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5055759831836657714</id><published>2008-11-26T11:27:00.008+08:00</published><updated>2009-03-18T10:50:45.448+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='encoding'/><category scheme='http://www.blogger.com/atom/ns#' term='utf'/><title type='text'>穿梭於 UTF-8 與 UTF-16 之間</title><content type='html'>偶然於 &lt;a href="http://www.7-zip.org/"&gt;7zip&lt;/a&gt; 的 &lt;a href="http://www.7-zip.org/download.html"&gt;LZMA sdk&lt;/a&gt; 裡發現非常簡潔的 &lt;a href="http://en.wikipedia.org/wiki/UTF-8"&gt;UTF-8&lt;/a&gt;/&lt;a href="http://en.wikipedia.org/wiki/UTF-16"&gt;UTF-16&lt;/a&gt; 變換函數，連一般轉換成 Unicode 的中介動作也省去了。可惜它本身的解壓功能未能滿足遊戲裝載系統的要求，皆因 7zip 的 archive 格式不能以最少的資源去解壓 archive 裡的個別檔案。&lt;br /&gt;以下源始碼引用 &lt;a href="http://www.7-zip.org/download.html"&gt;LZMA sdk&lt;/a&gt; 再加上本人所寫的額外錯誤偵測與註解，enjoy!&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;typedef byte_t unsigned char;&lt;br /&gt;&lt;br /&gt;// Reference: http://en.wikipedia.org/wiki/Utf8&lt;br /&gt;static const byte_t cUtf8Limits[] = {&lt;br /&gt; 0xC0, // Start of a 2-byte sequence&lt;br /&gt; 0xE0, // Start of a 3-byte sequence&lt;br /&gt; 0xF0, // Start of a 4-byte sequence&lt;br /&gt; 0xF8, // Start of a 5-byte sequence&lt;br /&gt; 0xFC, // Start of a 6-byte sequence&lt;br /&gt; 0xFE // Invalid: not defined by original UTF-8 specification&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;/*! Usually it is a 2 steps process to convert the string, invoke utf8ToUtf16() with&lt;br /&gt; dest equals to null so that it gives you destLen (not including null terminator),&lt;br /&gt; then allocate the destination with that amount of memory and call utf8ToUtf16() once&lt;br /&gt; again to perform the actual conversion. You can skip the first call if you sure&lt;br /&gt; the destination buffer is large enough to store the data.&lt;br /&gt;&lt;br /&gt; \note Here we assum sizeof(wchar_t) == 2&lt;br /&gt; \ref Modify from 7zip LZMA sdk&lt;br /&gt; */&lt;br /&gt;bool utf8ToUtf16(wchar_t* dest, size_t&amp;amp; destLen, const char* src, size_t maxSrcLen)&lt;br /&gt;{&lt;br /&gt; size_t destPos = 0, srcPos = 0;&lt;br /&gt;&lt;br /&gt; while(true)&lt;br /&gt; {&lt;br /&gt;  byte_t c; // Note that byte_t should be unsigned&lt;br /&gt;  size_t numAdds;&lt;br /&gt;&lt;br /&gt;  if(srcPos == maxSrcLen || src[srcPos] == '\0') {&lt;br /&gt;   if(dest &amp;amp;&amp;amp; destLen != destPos) {&lt;br /&gt;    assert(false &amp;amp;&amp;amp; "The provided destLen should equals to what we calculated here");&lt;br /&gt;    return false;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   destLen = destPos;&lt;br /&gt;   return true;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  c = src[srcPos++];&lt;br /&gt;&lt;br /&gt;  if(c &amp;lt; 0x80) { // 0-127, US-ASCII (single byte)&lt;br /&gt;   if(dest)&lt;br /&gt;    dest[destPos] = (wchar_t)c;&lt;br /&gt;   ++destPos;&lt;br /&gt;   continue;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if(c &amp;lt; 0xC0) // The first octet for each code point should within 0-191&lt;br /&gt;   break;&lt;br /&gt;&lt;br /&gt;  for(numAdds = 1; numAdds &amp;lt; 5; ++numAdds)&lt;br /&gt;   if(c &amp;lt; cUtf8Limits[numAdds])&lt;br /&gt;    break;&lt;br /&gt;  uint32_t value = c - cUtf8Limits[numAdds - 1];&lt;br /&gt;&lt;br /&gt;  do {&lt;br /&gt;   byte_t c2;&lt;br /&gt;   if(srcPos == maxSrcLen || src[srcPos] == '\0')&lt;br /&gt;    break;&lt;br /&gt;   c2 = src[srcPos++];&lt;br /&gt;   if(c2 &amp;lt; 0x80 || c2 &amp;gt;= 0xC0)&lt;br /&gt;    break;&lt;br /&gt;   value &amp;lt;&amp;lt;= 6;&lt;br /&gt;   value |= (c2 - 0x80);&lt;br /&gt;  } while(--numAdds != 0);&lt;br /&gt;&lt;br /&gt;  if(value &amp;lt; 0x10000) {&lt;br /&gt;   if(dest)&lt;br /&gt;    dest[destPos] = (wchar_t)value;&lt;br /&gt;   ++destPos;&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   value -= 0x10000;&lt;br /&gt;   if(value &amp;gt;= 0x100000)&lt;br /&gt;    break;&lt;br /&gt;   if(dest) {&lt;br /&gt;    dest[destPos + 0] = (wchar_t)(0xD800 + (value &amp;gt;&amp;gt; 10));&lt;br /&gt;    dest[destPos + 1] = (wchar_t)(0xDC00 + (value &amp;amp; 0x3FF));&lt;br /&gt;   }&lt;br /&gt;   destPos += 2;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; destLen = destPos;&lt;br /&gt; return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;bool utf8ToWStr(const char* utf8Str, size_t maxCount, std::wstring&amp;amp; wideStr)&lt;br /&gt;{&lt;br /&gt; size_t destLen = 0;&lt;br /&gt;&lt;br /&gt; // Get the length of the wide string&lt;br /&gt; if(!utf8ToUtf16(nullptr, destLen, utf8Str, maxCount))&lt;br /&gt;  return false;&lt;br /&gt;&lt;br /&gt; wideStr.resize(destLen);&lt;br /&gt; if(wideStr.size() != destLen)&lt;br /&gt;  return false;&lt;br /&gt;&lt;br /&gt; return utf8ToUtf16(const_cast&amp;lt;wchar_t*&amp;gt;(wideStr.c_str()), destLen, utf8Str, maxCount);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;bool utf8ToWStr(const std::string&amp;amp; utf8Str, std::wstring&amp;amp; wideStr)&lt;br /&gt;{&lt;br /&gt; return utf8ToWStr(utf8Str.c_str(), utf8Str.size(), wideStr);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//! See the documentation for utf8ToUtf16()&lt;br /&gt;bool utf16ToUtf8(char* dest, size_t&amp;amp; destLen, const wchar_t* src, size_t maxSrcLen)&lt;br /&gt;{&lt;br /&gt; size_t destPos = 0, srcPos = 0;&lt;br /&gt;&lt;br /&gt; while(true)&lt;br /&gt; {&lt;br /&gt;  uint32_t value;&lt;br /&gt;  size_t numAdds;&lt;br /&gt;&lt;br /&gt;  if(srcPos == maxSrcLen || src[srcPos] == L'\0') {&lt;br /&gt;   if(dest &amp;amp;&amp;amp; destLen != destPos) {&lt;br /&gt;    assert(false &amp;amp;&amp;amp; "The provided destLen should equals to what we calculated here");&lt;br /&gt;    return false;&lt;br /&gt;   }&lt;br /&gt;   destLen = destPos;&lt;br /&gt;   return true;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  value = src[srcPos++];&lt;br /&gt;&lt;br /&gt;  if(value &amp;lt; 0x80) { // 0-127, US-ASCII (single byte)&lt;br /&gt;   if(dest)&lt;br /&gt;    dest[destPos] = char(value);&lt;br /&gt;   ++destPos;&lt;br /&gt;   continue;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if(value &amp;gt;= 0xD800 &amp;amp;&amp;amp; value &amp;lt; 0xE000) {&lt;br /&gt;   if(value &amp;gt;= 0xDC00 || srcPos == maxSrcLen)&lt;br /&gt;    break;&lt;br /&gt;   uint32_t c2 = src[srcPos++];&lt;br /&gt;   if(c2 &amp;lt; 0xDC00 || c2 &amp;gt;= 0xE000)&lt;br /&gt;    break;&lt;br /&gt;   value = ((value - 0xD800) &amp;lt;&amp;lt; 10) | (c2 - 0xDC00);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  for(numAdds = 1; numAdds &amp;lt; 5; ++numAdds)&lt;br /&gt;   if(value &amp;lt; (uint32_t(1) &amp;lt;&amp;lt; (numAdds * 5 + 6)))&lt;br /&gt;    break;&lt;br /&gt;&lt;br /&gt;  if(dest)&lt;br /&gt;   dest[destPos] = char(cUtf8Limits[numAdds - 1] + (value &amp;gt;&amp;gt; (6 * numAdds)));&lt;br /&gt;  ++destPos;&lt;br /&gt;&lt;br /&gt;  do {&lt;br /&gt;   --numAdds;&lt;br /&gt;   if(dest)&lt;br /&gt;    dest[destPos] = char(0x80 + ((value &amp;gt;&amp;gt; (6 * numAdds)) &amp;amp; 0x3F));&lt;br /&gt;   ++destPos;&lt;br /&gt;  } while(numAdds != 0);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; destLen = destPos;&lt;br /&gt; return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;bool wStrToUtf8(const wchar_t* wideStr, size_t maxCount, std::string&amp;amp; utf8Str)&lt;br /&gt;{&lt;br /&gt; size_t destLen = 0;&lt;br /&gt;&lt;br /&gt; // Get the length of the utf-8 string&lt;br /&gt; if(!utf16ToUtf8(nullptr, destLen, wideStr, maxCount))&lt;br /&gt;  return false;&lt;br /&gt;&lt;br /&gt; utf8Str.resize(destLen);&lt;br /&gt; if(utf8Str.size() != destLen)&lt;br /&gt;  return false;&lt;br /&gt;&lt;br /&gt; return utf16ToUtf8(const_cast&amp;lt;char*&amp;gt;(utf8Str.c_str()), destLen, wideStr, maxCount);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;bool wStrToUtf8(const std::wstring&amp;amp; wideStr, std::string&amp;amp; utf8Str)&lt;br /&gt;{&lt;br /&gt; return wStrToUtf8(wideStr.c_str(), wideStr.size(), utf8Str);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5055759831836657714?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5055759831836657714/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/11/utf-8-utf-16.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5055759831836657714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5055759831836657714'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/11/utf-8-utf-16.html' title='穿梭於 UTF-8 與 UTF-16 之間'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-1201590910502657238</id><published>2008-11-20T11:09:00.011+08:00</published><updated>2008-11-20T12:47:44.253+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='simplicity'/><category scheme='http://www.blogger.com/atom/ns#' term='philosophy'/><title type='text'>編程花招的謎思</title><content type='html'>微軟快要推出下一代 &lt;a href="http://www.microsoft.com/visualstudio/2010/overview.mspx"&gt;Visual Studio 2010&lt;/a&gt;，它對於 &lt;a href="http://en.wikipedia.org/wiki/C%2B%2B0x"&gt;C++0x&lt;/a&gt; 的支持最令我期待。&lt;br /&gt;儘管 C++0x compiler 還未成熟與普及，已有工程師把弄新的語法，創造耀眼&lt;a href="http://blogs.msdn.com/vcblog/archive/2008/11/18/stupid-lambda-tricks.aspx"&gt;花招&lt;/a&gt;。&lt;br /&gt;其實我也非常喜歡耍玩語法上的把戲，但我亦知道它會帶來什麼災害。&lt;br /&gt;以下文字引述自&lt;a href="http://blogs.msdn.com/vcblog/archive/2008/11/18/stupid-lambda-tricks.aspx"&gt;花招&lt;/a&gt;裡的一篇回覆，也是我心裡想說的：&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;Interesting acrobatics, but I am a &lt;a href="http://en.wikipedia.org/wiki/KISS_principle"&gt;KISS&lt;/a&gt; fan.&lt;br /&gt;&lt;br /&gt;I prefer not to mandate a C++ black belt (with several Dans on occassion) on coworkers who try to understand and modify my code, so thanks but I'll pass.&lt;br /&gt;&lt;br /&gt;Is there anything in the above code that cannot be done in plain C in a way that&lt;span style="font-weight: bold;"&gt; 90% of the dev population can understand and 80% can modify/extend without a mistake&lt;/span&gt;?&lt;br /&gt;&lt;br /&gt;Why do architects &lt;span style="font-weight: bold;"&gt;feel so compelled to save the world by providing infrastructure and plumbing for everything conceivable under the sun&lt;/span&gt;?&lt;br /&gt;&lt;br /&gt;What about memoization? If I am in such a corner case where caching the results of a function call will *actually* improve performance, what makes you think I would opt for an obscure and totally incomprehensible generic template that I cannot understand or debug, rather than a custom-tailored, totally non-reusable, top-performing, totally understandable and debugable solution?&lt;br /&gt;&lt;br /&gt;Don't get me wrong, I am not an anti-STL, do-it-yourself (CMyHashTable, CMyDynamicArray, CMyOS) gangho. I am just a KISS fan (including the rock band). &lt;span style="font-weight: bold;"&gt;If something can be done in a way that is simpler, easier to understand, debug and extend, then I prefer the simpler way&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;I just get so frustrated when people &lt;span style="font-weight: bold;"&gt;do all this acrobatic stuff in production code &lt;/span&gt;&lt;span style="font-weight: bold;"&gt;just because (a) they can do it (b) it's cool to do it, without thinking back a lil'bit&lt;/span&gt; or actually having mastered the 'tools' they are using.&lt;br /&gt;&lt;br /&gt;A similar example is 'patternitis'. I have seen&lt;span style="font-weight: bold;"&gt; countless C++ freshmen reading the GangOf4 Design Patterns book and then creating a total mess in everything&lt;/span&gt;, like deciding to implement the Visitor pattern on a problem that required Composite and ended up coding a third pattern alltogether from the same book, still naming the classes CVisitorXYZ (probably they opened the book on the wrong page at some point).&lt;br /&gt;&lt;br /&gt;I have met exactly 1 guy (I called him the "Professor") who knew C++ well enough and had the knowledge to apply the patterns where they ought to be applied. His code was a masterpiece, it worked like a breeze, but when he left, none else in the house could figure things out.&lt;br /&gt;&lt;br /&gt;So what's the point with these Lambda stuff really? Increase the expression of the language? &lt;span style="font-weight: bold;"&gt;Are we doing poetry or software?&lt;/span&gt; Why should we turn simple code that everyone understands into more and more elegant and concise code that only few can understand and make it work?&lt;br /&gt;&lt;br /&gt;I have been coding in C (drivers) and C++ for 15 years and not once was I trapped because I was missing lambda expressions or similar syntactic gizmos.&lt;br /&gt;&lt;br /&gt;So what's the point really? Please enlighten me. I don't say that *I* am right and *YOU* are wrong. I am saying that I don't see, I don't understand the positive value that these things bring in that far outweighs the problems they cause by complicating the language.&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;當然，流行/藝術派與實際派的存在都是有意義的；否則編程世界不是一團糟就是停滯不前。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-1201590910502657238?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/1201590910502657238/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/11/c0x.html#comment-form' title='3 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/1201590910502657238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/1201590910502657238'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/11/c0x.html' title='編程花招的謎思'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5021732704160660301</id><published>2008-11-06T15:15:00.008+08:00</published><updated>2008-11-06T15:47:05.667+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hell'/><title type='text'>沒有惡意的 Bonjour</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SRKakAbFLNI/AAAAAAAAAEw/74HNAvMT09s/s1600-h/Bonjour+service.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; cursor: pointer; width: 320px; height: 195px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SRKakAbFLNI/AAAAAAAAAEw/74HNAvMT09s/s320/Bonjour+service.png" alt="" id="BLOGGER_PHOTO_ID_5265440857729084626" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;無意中在視窗服務裏面發現多了一個服務項，進程為 mDNSResponder.exe。發現時就感覺不妙，還以為是木馬。&lt;br /&gt;&lt;br /&gt;原來它不是病毒或惡意程式，一個名為 Bonjour 的服務，是 Apple 公司的產品。一般會在安裝 Adobe CS3 後出現；用於自動發現局域網上的印表機或其他設備，一般沒什麼用處，卸載後也不影響其他軟體的使用，下面是 &lt;a href="http://www.adobe.com/go/kb400982"&gt;Adobe&lt;/a&gt; 網站上公佈的卸載方法：&lt;br /&gt;&lt;ol&gt;&lt;li&gt;運行 C:\Program Files\Bonjour\mDNSResponder.exe -remove&lt;/li&gt;&lt;li&gt;重命名 C:\Program Files\Bonjour\mdnsNSP.dll 為 mdnsNSP.old&lt;/li&gt;&lt;li&gt;重啟電腦&lt;/li&gt;&lt;li&gt;刪除 C:\Program Files\Bonjour 目錄&lt;/li&gt;&lt;/ol&gt;[註] Bonjour 在法語中解作 "你好"。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5021732704160660301?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5021732704160660301/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/11/bonjour.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5021732704160660301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5021732704160660301'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/11/bonjour.html' title='沒有惡意的 Bonjour'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_4CNEj03jaok/SRKakAbFLNI/AAAAAAAAAEw/74HNAvMT09s/s72-c/Bonjour+service.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-7242994492519626600</id><published>2008-10-31T18:33:00.007+08:00</published><updated>2009-03-18T10:54:20.424+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shadow mapping'/><category scheme='http://www.blogger.com/atom/ns#' term='glsl'/><category scheme='http://www.blogger.com/atom/ns#' term='opengl'/><title type='text'>陰影映射</title><content type='html'>完成了&lt;a href="http://mtlung.blogspot.com/2008/10/blog-post.html"&gt;紋理投影&lt;/a&gt;後，製作基本陰影映射就如吃生&lt;span id="dropdownid"&gt;菜&lt;/span&gt;&lt;span id="dropdownid"&gt;一樣&lt;/span&gt;容易。&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SQrfZ26s9TI/AAAAAAAAAEo/PopHpzdb7Qo/s1600-h/ShadowMap.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: none; cursor: pointer; width: 320px; height: 249px;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SQrfZ26s9TI/AAAAAAAAAEo/PopHpzdb7Qo/s320/ShadowMap.jpg" alt="" id="BLOGGER_PHOTO_ID_5263264749867300146" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;// Pixel shader&lt;br /&gt;&lt;br /&gt;varying vec3 normal, lightDir, halfVector;&lt;br /&gt;varying vec2 colorCoord;&lt;br /&gt;varying vec4 shadowCoord;&lt;br /&gt;uniform sampler2D colorTex;&lt;br /&gt;uniform sampler2DShadow shadowTex;&lt;br /&gt;&lt;br /&gt;// Light intensity inside shadow&lt;br /&gt;const float shadowIntensity = 0.5;&lt;br /&gt;&lt;br /&gt;// Should be supplied as uniform&lt;br /&gt;const float shadowMapPixelScale = 1.0 / float(2048);&lt;br /&gt;const int pcfSize = 1; // The pcf filtering size, 0 -&amp;gt; 1x1 (no filtering), 1 -&amp;gt; 3x3 etc&lt;br /&gt;&lt;br /&gt;void main(void)&lt;br /&gt;{&lt;br /&gt; vec4 diffuse = gl_FrontLightProduct[0].diffuse;&lt;br /&gt; vec4 specular = gl_FrontLightProduct[0].specular;&lt;br /&gt; vec4 ambient = gl_FrontLightProduct[0].ambient;&lt;br /&gt; vec3 n = normalize(normal);&lt;br /&gt; float NdotL = max(dot(n, lightDir), 0.0);&lt;br /&gt;&lt;br /&gt; diffuse *= NdotL;&lt;br /&gt; vec3 halfV = normalize(halfVector);&lt;br /&gt; float NdotHV = max(dot(n, halfV), 0.0);&lt;br /&gt; specular *= pow(NdotHV, gl_FrontMaterial.shininess);&lt;br /&gt;&lt;br /&gt; // Get the shadow value, let the hardware perform perspective divide,&lt;br /&gt; // depth comparison and 2x2 pcf if supported.&lt;br /&gt; // float shadowValue = shadow2DProj(shadowTex, shadowCoord).r;&lt;br /&gt;&lt;br /&gt; // Perform PCF filtering&lt;br /&gt; float shadowValue = 0.0;&lt;br /&gt; for(int i=-pcfSize; i&amp;lt;=pcfSize; ++i) for(int j=-pcfSize; j&amp;lt;=pcfSize; ++j)&lt;br /&gt; {&lt;br /&gt;  vec4 offset = vec4(i * shadowMapPixelScale, j * shadowMapPixelScale, 0, 0);&lt;br /&gt;  shadowValue += shadow2DProj(shadowTex, shadowCoord + offset).r;&lt;br /&gt; }&lt;br /&gt; shadowValue /= (2 * pcfSize + 1) * (2 * pcfSize + 1);&lt;br /&gt;&lt;br /&gt; float shadowSpecularFactor = shadowValue == 0 ? 0 : 1;&lt;br /&gt; float shadowDiffuseFactor = min(1.0, shadowIntensity + shadowValue);&lt;br /&gt;&lt;br /&gt; gl_FragData[0] = shadowSpecularFactor * specular +&lt;br /&gt; (ambient + shadowDiffuseFactor * diffuse) * vec4(texture2D(colorTex, colorCoord).xyz, 1); &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;當然，還有許多的陰影技術可以嘗試；我比較臨感興趣的有：&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.mpi-inf.mpg.de/%7Etannen/papers/gi_08_esm.pdf"&gt;Exponential Shadow Map&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.punkuser.net/vsm/"&gt;Variance Shadow Map&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.comp.nus.edu.sg/%7Etants/tsm.html"&gt;Trapezoidal Shadow Maps&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://appsrv.cse.cuhk.edu.hk/%7Efzhang/pssm_project/"&gt;Parallel-Split Shadow Maps&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;hr /&gt;&lt;span style="color: rgb(51, 102, 255);font-size:85%;" &gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-size:85%;"&gt;相關文章&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:85%;"&gt;&lt;a href="http://www.codesampler.com/oglsrc/oglsrc_8.htm"&gt;Code Sampler - Shadow mapping&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:85%;"&gt;&lt;a href="http://www.paulsprojects.net/tutorials/smt/smt.html"&gt;Paul's Project - Shadow mapping&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:85%;"&gt;&lt;a href="http://developer.download.nvidia.com/presentations/2008/GDC/http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf"&gt;GDC08 Advanced Soft Shadow Mapping Techniques&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-7242994492519626600?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/7242994492519626600/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/10/blog-post_31.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7242994492519626600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7242994492519626600'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/10/blog-post_31.html' title='陰影映射'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/SQrfZ26s9TI/AAAAAAAAAEo/PopHpzdb7Qo/s72-c/ShadowMap.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-8199178753707400979</id><published>2008-10-31T12:47:00.007+08:00</published><updated>2009-03-18T11:00:17.435+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='projective texture'/><category scheme='http://www.blogger.com/atom/ns#' term='glsl'/><category scheme='http://www.blogger.com/atom/ns#' term='opengl'/><title type='text'>紋理投影</title><content type='html'>為了製作陰影映射 (Shadow mapping)，先來一個紋理投影 (Projective texture)。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SQqbi3qwEUI/AAAAAAAAAEg/6Z3gFY5hMCw/s1600-h/projectiveTexture.JPG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: none; cursor: pointer; width: 373px; height: 290px;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SQqbi3qwEUI/AAAAAAAAAEg/6Z3gFY5hMCw/s320/projectiveTexture.JPG" alt="" id="BLOGGER_PHOTO_ID_5263190137896964418" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SQqaDInBfnI/AAAAAAAAAEY/d-OkcSfnSTg/s1600-h/ProjectiveTransform.PNG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: none; cursor: pointer; width: 196px; height: 289px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SQqaDInBfnI/AAAAAAAAAEY/d-OkcSfnSTg/s320/ProjectiveTransform.PNG" alt="" id="BLOGGER_PHOTO_ID_5263188493177290354" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;製作紋理投影的關鍵是紋理投影矩陣，是它把物體的世界座標轉換成投影空間的紋理座標；以下是 OpenGL fixed pipeline 的實作：&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;// The following code assums you have already applied the camera's view matrix&lt;br /&gt;// to the model-view matrix stack&lt;br /&gt;setupViewMatrix();&lt;br /&gt;&lt;br /&gt;// Use another texture unit to avoid conflit with the color texture of the model&lt;br /&gt;glActiveTexture(GL_TEXTURE1);&lt;br /&gt;&lt;br /&gt;// You can choose between GL_MODULATE and GL_ADD&lt;br /&gt;glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);&lt;br /&gt;&lt;br /&gt;// Matrix44 is just a simple matrix 4x4 class, but remember opengl use column major layout&lt;br /&gt;// The bias matrix is to map from clip space [-1, 1] to texture space [0, 1]&lt;br /&gt;Matrix44 biasMatrix = Matrix44(&lt;br /&gt; 0.5f, 0, 0, 0.5f,&lt;br /&gt; 0, 0.5f, 0, 0.5f,&lt;br /&gt; 0, 0, 0.5f, 0.5f,&lt;br /&gt; 0, 0, 0,    1.0f);&lt;br /&gt;&lt;br /&gt;Matrix44 projectorProjection, projectorView;&lt;br /&gt;&lt;br /&gt;// Setup projectorProjection and projectorView according to&lt;br /&gt;// how you want the texture to be projected on the scene&lt;br /&gt;// ...&lt;br /&gt;&lt;br /&gt;Matrix44 textureMatrix = biasMatrix * projectorProjection * projectorView;&lt;br /&gt;&lt;br /&gt;// Preform a transpose so that we get the rows of the matrix rather than columns&lt;br /&gt;textureMatrix = textureMatrix.transpose();&lt;br /&gt;&lt;br /&gt;// A post-multiply by the inverse of the CURRENT modelview matrix is applied&lt;br /&gt;// by opengl automatically to the eye plane equations we provide.&lt;br /&gt;// Therefor, it is important to enable these texture coordinate generation&lt;br /&gt;// before appling any model-world matrix transform&lt;br /&gt;glTexGenfv(GL_S, GL_EYE_PLANE, textureMatrix[0]); // Row 0&lt;br /&gt;glTexGenfv(GL_T, GL_EYE_PLANE, textureMatrix[1]); // Row 1&lt;br /&gt;glTexGenfv(GL_R, GL_EYE_PLANE, textureMatrix[2]); // Row 2&lt;br /&gt;glTexGenfv(GL_Q, GL_EYE_PLANE, textureMatrix[3]); // Row 3&lt;br /&gt;&lt;br /&gt;glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);&lt;br /&gt;glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);&lt;br /&gt;glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);&lt;br /&gt;glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);&lt;br /&gt;&lt;br /&gt;// Enable automatic texture coordinate generation&lt;br /&gt;// Note that the R and Q component may not be used in simple projective texture&lt;br /&gt;// but they are needed for shadow mapping&lt;br /&gt;glEnable(GL_TEXTURE_GEN_S);&lt;br /&gt;glEnable(GL_TEXTURE_GEN_T);&lt;br /&gt;glEnable(GL_TEXTURE_GEN_R);&lt;br /&gt;glEnable(GL_TEXTURE_GEN_Q);&lt;br /&gt;&lt;br /&gt;// Bind the projector's texture&lt;br /&gt;glBindTexture(GL_TEXTURE_2D, textureHandle);&lt;br /&gt;&lt;br /&gt;// You may move the clamp setting to where you initialize the texture&lt;br /&gt;// rather than setting up every frame&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP);&lt;br /&gt;&lt;br /&gt;// Set the active texture back to the model's color texture&lt;br /&gt;glActiveTexture(GL_TEXTURE0);&lt;br /&gt;&lt;br /&gt;// For each model:&lt;br /&gt;//  Apply any world transform for your model&lt;br /&gt;//  Draw the model&lt;br /&gt;// End&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;當我試圖把上述的&lt;span&gt;編&lt;/span&gt;碼轉移到 glsl，我遇到了一個沒有多被紋理投影項目中詳述的問題，那就是如何在  glsl 裡得到物體的世界座標。這個問題沒有在 fixed pipeline 中出現是因為早於應用物體 - 世界矩陣 (Model-world matrix) 之前，那紋理投影矩陣已計算恰當。縱然 glsl (其實是整個 OpenGL) 沒有單獨的物體 - 世界矩陣可供查詢，我們可以把攝像機的視圖&lt;span style="font-weight: bold;"&gt;逆&lt;/span&gt;矩陣乘以 gl_ModelViewMatrix 求出物體 - 世界矩陣。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;glActiveTexture(GL_TEXTURE1);&lt;br /&gt;&lt;br /&gt;Matrix44 biasMatrix = Matrix44(&lt;br /&gt; 0.5f, 0, 0, 0.5f,&lt;br /&gt; 0, 0.5f, 0, 0.5f,&lt;br /&gt; 0, 0, 0.5f, 0.5f,&lt;br /&gt; 0, 0, 0,    1.0f);&lt;br /&gt;&lt;br /&gt;// We need the camera's view matrix inverse in order to obtain the model-world&lt;br /&gt;// transform in glsl&lt;br /&gt;Matrix44 projectorProjection, projectorView, cameraView;&lt;br /&gt;&lt;br /&gt;// Setup projectorProjection, projectorView and cameraView&lt;br /&gt;// ...&lt;br /&gt;&lt;br /&gt;Matrix44 textureMatrix =&lt;br /&gt;biasMatrix * projectorProjection * projectorView * cameraView.inverse();&lt;br /&gt;&lt;br /&gt;// Set up the texture matrix&lt;br /&gt;glMatrixMode(GL_TEXTURE);&lt;br /&gt;glLoadMatrixf(textureMatrix.getPtr());&lt;br /&gt;glMatrixMode(GL_MODELVIEW);&lt;br /&gt;&lt;br /&gt;glBindTexture(GL_TEXTURE_2D, textureHandle);&lt;br /&gt;&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);&lt;br /&gt;glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP);&lt;br /&gt;&lt;br /&gt;glActiveTexture(GL_TEXTURE0);&lt;br /&gt;&lt;br /&gt;// For each model:&lt;br /&gt;//  Apply any world transform for your model&lt;br /&gt;//  Draw the model&lt;br /&gt;// End&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// Vertex shader:&lt;br /&gt;varying vec3 normal, lightDir, halfVector;&lt;br /&gt;varying vec2 colorCoord;&lt;br /&gt;varying vec4 projectiveCoord;&lt;br /&gt;&lt;br /&gt;void main(void)&lt;br /&gt;{&lt;br /&gt; gl_Position = ftransform();&lt;br /&gt; normal = gl_NormalMatrix * gl_Normal;&lt;br /&gt; lightDir = normalize(gl_LightSource[0].position.xyz);&lt;br /&gt; halfVector = normalize(gl_LightSource[0].halfVector.xyz);&lt;br /&gt;&lt;br /&gt; colorCoord = gl_MultiTexCoord0.xy;&lt;br /&gt;&lt;br /&gt; // gl_TextureMatrix[1] should contains the inverse of the view matrix,&lt;br /&gt; // resulting a model matrix when combining with gl_ModelViewMatrix&lt;br /&gt; projectiveCoord = gl_TextureMatrix[1] * gl_ModelViewMatrix * gl_Vertex;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Pixel shader:&lt;br /&gt;varying vec3 normal, lightDir, halfVector;&lt;br /&gt;varying vec2 colorCoord;&lt;br /&gt;varying vec4 projectiveCoord;&lt;br /&gt;uniform sampler2D colorTex;&lt;br /&gt;uniform sampler2D projectiveTex;&lt;br /&gt;&lt;br /&gt;void main(void)&lt;br /&gt;{&lt;br /&gt; vec4 diffuse = gl_FrontLightProduct[0].diffuse;&lt;br /&gt; vec4 specular = gl_FrontLightProduct[0].specular;&lt;br /&gt; vec4 ambient = gl_FrontLightProduct[0].ambient;&lt;br /&gt; vec3 n = normalize(normal);&lt;br /&gt; float NdotL = max(dot(n, lightDir), 0.0);&lt;br /&gt;&lt;br /&gt; diffuse *= NdotL;&lt;br /&gt; vec3 halfV = normalize(halfVector);&lt;br /&gt; float NdotHV = max(dot(n, halfV), 0.0);&lt;br /&gt; specular *= pow(NdotHV, gl_FrontMaterial.shininess);&lt;br /&gt;&lt;br /&gt; gl_FragData[0] = specular + (ambient + diffuse) * vec4(texture2D(colorTex, colorCoord).xyz, 1);&lt;br /&gt;&lt;br /&gt; // Apply the projective texture&lt;br /&gt; gl_FragData[0] += texture2DProj(projectiveTex, projectiveCoord);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-8199178753707400979?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/8199178753707400979/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/10/blog-post.html#comment-form' title='3 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/8199178753707400979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/8199178753707400979'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/10/blog-post.html' title='紋理投影'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/SQqbi3qwEUI/AAAAAAAAAEg/6Z3gFY5hMCw/s72-c/projectiveTexture.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-2821971763491542661</id><published>2008-10-13T18:18:00.009+08:00</published><updated>2008-10-14T09:54:22.871+08:00</updated><title type='text'>免費 Model 寶庫</title><content type='html'>今天無意中發現了一個&lt;a href="http://gfx-3d-model.blogspot.com/"&gt;博客&lt;/a&gt;非常慷慨地把大量高品質 (相對其他免費) 的 3D 模型分享給全世界。&lt;br /&gt;站內有不同種類的模型，但還是汽車的居多；雖然下載的方法有點煩，畢竟是免費的，好應該說聲多謝。&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SPMmANzmQ4I/AAAAAAAAAEQ/PU-tQMvKGtc/s1600-h/nsx_HP_small.jpg"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SPMmANzmQ4I/AAAAAAAAAEQ/PU-tQMvKGtc/s320/nsx_HP_small.jpg" alt="" id="BLOGGER_PHOTO_ID_5256586975219434370" border="0" /&gt;&lt;/a&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:78%;"&gt;http://i344.photobucket.com/albums/p338/free3dart/nsx_HP_small.jpg&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/SPMl7RM8MmI/AAAAAAAAAEI/hpbU6EPdBJs/s1600-h/f18.jpg"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_4CNEj03jaok/SPMl7RM8MmI/AAAAAAAAAEI/hpbU6EPdBJs/s320/f18.jpg" alt="" id="BLOGGER_PHOTO_ID_5256586890231689826" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:78%;"&gt;http://i344.photobucket.com/albums/p338/free3dart/f18.jpg&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-2821971763491542661?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/2821971763491542661/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/10/model.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2821971763491542661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2821971763491542661'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/10/model.html' title='免費 Model 寶庫'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/SPMmANzmQ4I/AAAAAAAAAEQ/PU-tQMvKGtc/s72-c/nsx_HP_small.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-5782848100826081688</id><published>2008-09-22T15:21:00.001+08:00</published><updated>2009-03-18T11:04:39.609+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shader'/><category scheme='http://www.blogger.com/atom/ns#' term='ssao'/><title type='text'>SSAO 新進展</title><content type='html'>Yeah! 利用了法線緩衝所提供的資訊後， SSAO (屏幕空間環境光遮蔽) 的效果迫真了許多。&lt;br /&gt;開始感受到電腦繪圖算法的迷人之處，可惜再沒有人和我分享這份喜悅&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SNhO8v1z9LI/AAAAAAAAADQ/8OpKr1sVcv8/s1600-h/sad.PNG"&gt;&lt;img style="margin: 0pt 0pt -5px 5px; cursor: pointer;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SNhO8v1z9LI/AAAAAAAAADQ/8OpKr1sVcv8/s320/sad.PNG" alt="sad" id="BLOGGER_PHOTO_ID_5249032171241600178" border="0" /&gt;&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/SNdI3i4L2-I/AAAAAAAAACg/s46sH55-wWY/s1600-h/SSAO6.png"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_4CNEj03jaok/SNdI3i4L2-I/AAAAAAAAACg/s46sH55-wWY/s320/SSAO6.png" alt="" id="BLOGGER_PHOTO_ID_5248744009816071138" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4CNEj03jaok/SNdI3tqBXQI/AAAAAAAAACo/DuTjDoXqIQ8/s1600-h/SSAO7.png"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_4CNEj03jaok/SNdI3tqBXQI/AAAAAAAAACo/DuTjDoXqIQ8/s320/SSAO7.png" alt="" id="BLOGGER_PHOTO_ID_5248744012709453058" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SNdI4N4yykI/AAAAAAAAADA/FROizEJOsdI/s1600-h/SSAO10.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SNdI4N4yykI/AAAAAAAAADA/FROizEJOsdI/s320/SSAO10.png" alt="" id="BLOGGER_PHOTO_ID_5248744021361347138" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/SNdJNPWRrkI/AAAAAAAAADI/TotPbyTWX5E/s1600-h/SSAO11.png"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_4CNEj03jaok/SNdJNPWRrkI/AAAAAAAAADI/TotPbyTWX5E/s320/SSAO11.png" alt="" id="BLOGGER_PHOTO_ID_5248744382530694722" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4CNEj03jaok/SNdI3w0cJQI/AAAAAAAAACw/J7K6M773btU/s1600-h/SSAO8.png"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_4CNEj03jaok/SNdI3w0cJQI/AAAAAAAAACw/J7K6M773btU/s320/SSAO8.png" alt="" id="BLOGGER_PHOTO_ID_5248744013558457602" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SNdI30J9-uI/AAAAAAAAAC4/wq2959rg-qY/s1600-h/SSAO9.png"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SNdI30J9-uI/AAAAAAAAAC4/wq2959rg-qY/s320/SSAO9.png" alt="" id="BLOGGER_PHOTO_ID_5248744014454061794" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;讓我嘗試簡單地解釋它的原理吧。&lt;br /&gt;螢幕中的每一像素都會和它周圍的 N 個像素作比較，比較時有兩個因數需要考慮&lt;br /&gt;&lt;ol&gt;&lt;li&gt;兩像素於三圍空間中的位置；深度較淺的像素會遮蔽較深的像素，而遮蔽的程度就取決於距離。&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SNhRKAF131I/AAAAAAAAADY/7chowl17Ir4/s1600-h/SSAO_distance_function.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: none; cursor: pointer;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SNhRKAF131I/AAAAAAAAADY/7chowl17Ir4/s320/SSAO_distance_function.png" alt="" id="BLOGGER_PHOTO_ID_5249034597965356882" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;兩像素的法線內積 (&lt;a href="http://en.wikipedia.org/wiki/Dot_product"&gt;Dot product&lt;/a&gt;)；面向面的像素會比面向同一方向的像素較接觸不到外來的光線。&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SNhU0RNorcI/AAAAAAAAADg/TlIPrOX6gpU/s1600-h/SSAO_normal.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SNhU0RNorcI/AAAAAAAAADg/TlIPrOX6gpU/s320/SSAO_normal.png" alt="" id="BLOGGER_PHOTO_ID_5249038622650838466" border="0" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;至於怎樣對周圍的 N 個像素取樣，就是整個算法中最令人頭痛的問題。當然取樣越多效果越理想，但實際經驗告訴大家 N 只可以不大於 32 左右。隨著取樣的數量受限，而又希望有比較廣闊的取樣範圍 (位置較遙遠的像素都可互相影響)，可用一些隨機取樣模式；不過暫時我只用了一個十字形的取樣模式，只要取樣範圍不太大是可以接受的。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;uniform sampler2DRect texColor; // Color texture&lt;br /&gt;uniform sampler2DRect texDepth; // Depth texture&lt;br /&gt;uniform sampler2DRect texNormal;// Normal texture&lt;br /&gt;uniform vec2 camerarange = vec2(1.0, 500);&lt;br /&gt;&lt;br /&gt;varying vec2 texCoord;&lt;br /&gt;const float aoCap = 1.0;&lt;br /&gt;float aoMultiplier = 1000.0;&lt;br /&gt;&lt;br /&gt;float pw = 1.0;  // Use (1.0 / screensize.x) for GL_TEXTURE2D&lt;br /&gt;float ph = 1.0;&lt;br /&gt;&lt;br /&gt;float readDepth(in vec2 coord)&lt;br /&gt;{&lt;br /&gt; float nearZ = camerarange.x;&lt;br /&gt; float farZ = camerarange.y;&lt;br /&gt; float posZ = texture2DRect(texDepth, coord).x;&lt;br /&gt;&lt;br /&gt; return (2.0 * nearZ) / (nearZ + farZ - posZ * (farZ - nearZ));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;vec3 readNormal(in vec2 coord)&lt;br /&gt;{&lt;br /&gt; return normalize(2 * (texture2DRect(texNormal, coord).xyz - 1));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;float compareDepths(in float depth1, in float depth2)&lt;br /&gt;{&lt;br /&gt; float depthDiff = depth1 - depth2;&lt;br /&gt; const float aorange = 10.0;  // Units in space the AO effect extends to (this gets divided by the camera far range)&lt;br /&gt; float diff = clamp(1.0 - depthDiff * (camerarange.y - camerarange.x) / aorange, 0.0, 1.0);&lt;br /&gt; return min(aoCap, max(0.0, depthDiff) * aoMultiplier) * diff;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;float calAO(float depth, vec3 normal, float dw, float dh)&lt;br /&gt;{&lt;br /&gt; vec2 coord = vec2(texCoord.x + dw, texCoord.y + dh);&lt;br /&gt; float angleFactor = 1 - dot(normal, readNormal(coord));&lt;br /&gt;&lt;br /&gt; if(length(normal) == 0)&lt;br /&gt; angleFactor = 0;&lt;br /&gt;&lt;br /&gt; return angleFactor * compareDepths(depth, readDepth(coord));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void main(void)&lt;br /&gt;{&lt;br /&gt; float depth = readDepth(texCoord);&lt;br /&gt; float ao = 0.0;&lt;br /&gt;&lt;br /&gt; vec3 normal = readNormal(texCoord);&lt;br /&gt;&lt;br /&gt; for(int i=0; i&amp;lt;8; ++i) {&lt;br /&gt;  ao += calAO(depth, normal, pw, ph);&lt;br /&gt;  ao += calAO(depth, normal, pw, -ph);&lt;br /&gt;  ao += calAO(depth, normal, -pw, ph);&lt;br /&gt;  ao += calAO(depth, normal, -pw, -ph);&lt;br /&gt;&lt;br /&gt;  pw *= 1.4;&lt;br /&gt;  ph *= 1.4;&lt;br /&gt;  aoMultiplier /= 1.5;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; ao *= 2.0;&lt;br /&gt;&lt;br /&gt; gl_FragColor = vec4(1.0 - ao) * texture2DRect(texColor, texCoord);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;span style="color: rgb(51, 102, 255);font-size:85%;" &gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-size:100%;"&gt;相關文章&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://mtlung.blogspot.com/2008/09/shader.html"&gt;初嚐 Shader 編程&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://ati.amd.com/developer/SIGGRAPH08/Chapter05-Filion-StarCraftII.pdf"&gt;Starcraft 2 - Effects &amp;amp; Techniques&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rgba.scenesp.org/iq/computer/articles/ssao/ssao.htm"&gt;iñigo quilez&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&amp;amp;Number=236805&amp;amp;fpart=1"&gt;Discussion on opengl.org&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=463075"&gt;Discussion on gamedev.net&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-5782848100826081688?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/5782848100826081688/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/09/ssao.html#comment-form' title='8 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5782848100826081688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/5782848100826081688'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/09/ssao.html' title='SSAO 新進展'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/SNhO8v1z9LI/AAAAAAAAADQ/8OpKr1sVcv8/s72-c/sad.PNG' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-7108910897743187458</id><published>2008-09-14T14:04:00.000+08:00</published><updated>2008-09-18T10:30:00.151+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shader'/><category scheme='http://www.blogger.com/atom/ns#' term='ssao'/><category scheme='http://www.blogger.com/atom/ns#' term='starcraft'/><category scheme='http://www.blogger.com/atom/ns#' term='blizzard'/><title type='text'>《星海爭霸2》引擎技術解析</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/SMy5qIhk8yI/AAAAAAAAACY/32UnLOY8xjU/s1600-h/Starcraft2_EffectAndTechnique.jpg"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_4CNEj03jaok/SMy5qIhk8yI/AAAAAAAAACY/32UnLOY8xjU/s320/Starcraft2_EffectAndTechnique.jpg" alt="" id="BLOGGER_PHOTO_ID_5245771799473746722" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;繼 &lt;a href="http://www.google.com.hk/url?sa=t&amp;amp;source=web&amp;amp;ct=res&amp;amp;cd=1&amp;amp;url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FCryENGINE2&amp;amp;ei=-7bMSLGeIovQ6gPFmpTMDw&amp;amp;usg=AFQjCNHXE5iAK8DE7f922oLqyJTa6-c17g&amp;amp;sig2=5Ob0tmWbVh6O351u9iLt7A"&gt;CryEngine 2&lt;/a&gt; 的 &lt;a href="http://www.google.com.hk/url?sa=t&amp;amp;source=web&amp;amp;ct=res&amp;amp;cd=1&amp;amp;url=http%3A%2F%2Fdelivery.acm.org%2F10.1145%2F1290000%2F1281671%2Fp97-mittring.pdf%3Fkey1%3D1281671%26key2%3D9942678811%26coll%3DACM%26dl%3DACM%26CFID%3D15151515%26CFTOKEN%3D618461&amp;amp;ei=WqrMSOuPE4T27AOlm4nRDw&amp;amp;usg=AFQjCNFz7EA0GJvOad5auUpXRZt0-0l2sQ&amp;amp;sig2=NCWRQYwRNeHFD_zIASaL5Q"&gt;&lt;em&gt;Finding Next Gen&lt;/em&gt;&lt;/a&gt; 之後，一向不與學術界為伍的 &lt;a href="http://www.google.com.hk/url?sa=t&amp;amp;source=web&amp;amp;ct=res&amp;amp;cd=1&amp;amp;url=http%3A%2F%2Fwww.blizzard.com%2F&amp;amp;ei=FrfMSN7-BYKm6wOB3JzRDw&amp;amp;usg=AFQjCNHPtbNPYVehmmjKd2Ebn-q8sRE9Cw&amp;amp;sig2=8LPBEz6KqzD9wVmfHWGIUw"&gt;Blizzard&lt;/a&gt; 也不甘示弱；於 Siggraph 08 發表了一篇&lt;a href="http://ati.amd.com/developer/SIGGRAPH08/Chapter05-Filion-StarCraftII.pdf"&gt;論文&lt;/a&gt;，當中的內容頗為深入。&lt;br /&gt;期望星海爭霸2可快&lt;span id="dropdownid"&gt;點推出。&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-7108910897743187458?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/7108910897743187458/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/09/2.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7108910897743187458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7108910897743187458'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/09/2.html' title='《星海爭霸2》引擎技術解析'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_4CNEj03jaok/SMy5qIhk8yI/AAAAAAAAACY/32UnLOY8xjU/s72-c/Starcraft2_EffectAndTechnique.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-7248911109607697082</id><published>2008-09-13T12:16:00.001+08:00</published><updated>2009-03-18T11:07:55.540+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shader'/><category scheme='http://www.blogger.com/atom/ns#' term='ssao'/><title type='text'>初嚐 Shader 編程</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SMs_FPRG-nI/AAAAAAAAACA/0Vv15O_3A1M/s1600-h/Bike1.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SMs_FPRG-nI/AAAAAAAAACA/0Vv15O_3A1M/s320/Bike1.png" alt="" id="BLOGGER_PHOTO_ID_5245355550233262706" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SMs_FT_Z6JI/AAAAAAAAACI/md7aagXbkCU/s1600-h/Bike2.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SMs_FT_Z6JI/AAAAAAAAACI/md7aagXbkCU/s320/Bike2.png" alt="" id="BLOGGER_PHOTO_ID_5245355551501183122" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;完成基本的 Shader &lt;span style="font-weight: normal;"&gt;類別&lt;/span&gt;後，一口氣連 &lt;a href="http://www.blogger.com/www.gamedev.net/reference/articles/article2333.asp"&gt;Multiple Render Target&lt;/a&gt; (MRT) 和 &lt;a href="http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&amp;amp;Number=236698&amp;amp;fpart=1"&gt;Screen Space Ambient Occlusion&lt;/a&gt; (SSAO) 都攪定了。這叫 SSAO 的技術是近一年電腦遊戲繪圖領域的新寵兒；它的原理是利用深度緩衝 (Depth Buffer) 計算出當前考慮中的像素和它周圍的像素，於三圍空間中的相互關係，再加上法線緩衝的話，就可以知道這像素有沒有被其他像素所 "遮蔽"。&lt;br /&gt;&lt;br /&gt;暫時我的實作只用上了深度緩衝，算不上真正的 SSAO，至多是一個邊緣強調器；但出來的效果也不錯，可凸顯出物件的層次感。現有的實作會繼續改進之餘，亦會留下來給低級別的顯示卡使用。&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Vertex shader code:&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;// Screen space ambient occlusion&lt;br /&gt;// Reference:&lt;br /&gt;// http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&amp;amp;Number=236698&amp;amp;fpart=1&lt;br /&gt;// http://www.4gamer.net/games/047/G004713/20080223007/screenshot.html?num=002&lt;br /&gt;// http://rgba.scenesp.org/iq/computer/articles/ssao/ssao.htm&lt;br /&gt;// http://meshula.net/wordpress/?p=145&lt;br /&gt;&lt;br /&gt;varying vec2 texCoord;&lt;br /&gt;&lt;br /&gt;void main(void)&lt;br /&gt;{&lt;br /&gt; gl_Position = ftransform();&lt;br /&gt; texCoord = gl_MultiTexCoord0.xy;&lt;br /&gt; gl_FrontColor = gl_Color;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Pixel shader code:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;uniform sampler2D texColor;  // Color texture&lt;br /&gt;uniform sampler2D texDepth;  // Depth texture&lt;br /&gt;&lt;br /&gt;uniform vec2 camerarange = vec2(1.0, 500);&lt;br /&gt;uniform vec2 screensize;&lt;br /&gt;&lt;br /&gt;varying vec2 texCoord;&lt;br /&gt;&lt;br /&gt;float readDepth(in vec2 coord)&lt;br /&gt;{&lt;br /&gt; return (2.0 * camerarange.x) /&lt;br /&gt; (camerarange.y + camerarange.x - texture2D(texDepth, coord).x * (camerarange.y - camerarange.x));  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void main(void)&lt;br /&gt;{&lt;br /&gt; float depth = readDepth(texCoord);&lt;br /&gt; float d;&lt;br /&gt;&lt;br /&gt; float pw = 1.0 / screensize.x;&lt;br /&gt; float ph = 1.0 / screensize.y;&lt;br /&gt;&lt;br /&gt; float aoCap = 1.0;&lt;br /&gt;&lt;br /&gt; float ao = 0.0;&lt;br /&gt;&lt;br /&gt; float aoMultiplier = 1000.0;&lt;br /&gt;&lt;br /&gt; float depthTolerance = 0.0001;&lt;br /&gt;&lt;br /&gt; for(int i=0; i&amp;lt;4; ++i)&lt;br /&gt; {&lt;br /&gt;  d = readDepth(vec2(texCoord.x + pw, texCoord.y + ph));&lt;br /&gt;  ao += min(aoCap, max(0.0, depth - d - depthTolerance) * aoMultiplier);&lt;br /&gt;&lt;br /&gt;  d = readDepth(vec2(texCoord.x - pw, texCoord.y + ph));&lt;br /&gt;  ao += min(aoCap, max(0.0, depth - d - depthTolerance) * aoMultiplier);&lt;br /&gt;&lt;br /&gt;  d=readDepth(vec2(texCoord.x + pw, texCoord.y - ph));&lt;br /&gt;  ao += min(aoCap, max(0.0, depth - d - depthTolerance) * aoMultiplier);&lt;br /&gt;&lt;br /&gt;  d = readDepth(vec2(texCoord.x - pw, texCoord.y - ph));&lt;br /&gt;  ao += min(aoCap, max(0.0, depth - d - depthTolerance) * aoMultiplier);&lt;br /&gt;&lt;br /&gt;  pw *= 2.0;&lt;br /&gt;  ph *= 2.0;&lt;br /&gt;  aoMultiplier /= 2.0;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; ao /= 16.0;&lt;br /&gt;&lt;br /&gt; gl_FragColor = vec4(1.0 - ao) * texture2D(texColor, texCoord);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;最後還有一些未解決的問題，是關於 MRT 的；話說有些顯示卡並未支援非二乘方大小的材質緩衝，因此有必要使用 &lt;a href="http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt"&gt;GL_TEXTURE_RECTANGLE_ARB&lt;/a&gt;，可惜用了這材質格式後 Pixel Shader 又神奇地把遮蔽量計錯了。看來 Shader 的除錯方法還要好好領會。&lt;br /&gt;&lt;br /&gt;還有，材質緩衝是不支援 &lt;a href="http://en.wikipedia.org/wiki/Anti-aliasing"&gt;Fullscreen Anti-aliasing&lt;/a&gt; (FSAA) 的，這可以怎樣解決哩？&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-7248911109607697082?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/7248911109607697082/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/09/shader.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7248911109607697082'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7248911109607697082'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/09/shader.html' title='初嚐 Shader 編程'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/SMs_FPRG-nI/AAAAAAAAACA/0Vv15O_3A1M/s72-c/Bike1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-361030745495762191</id><published>2008-09-13T11:20:00.001+08:00</published><updated>2009-03-18T11:14:14.787+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='3ds'/><category scheme='http://www.blogger.com/atom/ns#' term='loading'/><category scheme='http://www.blogger.com/atom/ns#' term='0x4160'/><title type='text'>3DS轉換矩陣</title><content type='html'>經過半天的勞力，終於成功載入轉換矩陣 (0x4160 trunk)，全靠 &lt;a href="http://www.lib3ds.org/"&gt;lib3ds&lt;/a&gt; 的原碼。&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;// Trunk 0x4160 comes before 0x4120,&lt;br /&gt;// during the loading of 0x4160 we got the information to&lt;br /&gt;// change the clockwise/anti-clockwise triangle winding or not&lt;br /&gt;// and apply this information during the loading of 0x4120&lt;br /&gt;bool invertTriangleWinding = false;&lt;br /&gt;&lt;br /&gt;// ...&lt;br /&gt;&lt;br /&gt;// Loading the local coordinates chunk&lt;br /&gt;// It's base from lib3ds: http://www.lib3ds.org&lt;br /&gt;case 0x4160:&lt;br /&gt;{&lt;br /&gt; // We are using row major matrix&lt;br /&gt; Mat44f matrix = Mat44f::cIdentity;&lt;br /&gt; for(size_t i=0; i&amp;lt;4; ++i)&lt;br /&gt;  mStream-&amp;gt;read(matrix.row[i], sizeof(float) * 3);&lt;br /&gt;&lt;br /&gt; // Flip X coordinate of vertices if mesh matrix has negative determinant&lt;br /&gt; if((invertWinding = (matrix.determinant() &amp;lt; 0)) == true) {&lt;br /&gt;  Mat44f inv = matrix.inverse();&lt;br /&gt;&lt;br /&gt;  matrix.m00 = -matrix.m00;&lt;br /&gt;  matrix.m01 = -matrix.m01;&lt;br /&gt;  matrix.m02 = -matrix.m02;&lt;br /&gt;  matrix.m03 = -matrix.m03;&lt;br /&gt;&lt;br /&gt;  matrix = (inv * matrix).transpose();&lt;br /&gt;&lt;br /&gt;  size_t vertexCount = getVertexCount();&lt;br /&gt;  Vec3f* vertex = getVertexPointer();&lt;br /&gt;&lt;br /&gt;  for(size_t i=0; i&amp;lt;vertexCount; ++i) {&lt;br /&gt;   // Transform tmp using matrix, where matrix[3] holds the translation&lt;br /&gt;#ifdef FLIP_YZ_AXIS&lt;br /&gt;   Vec4f tmp(vertex[i].x, -vertex[i].z, vertex[i].y, 0);&lt;br /&gt;   tmp = (matrix * tmp) + matrix[3];&lt;br /&gt;   vertex[i] = Vec3f(tmp.x, tmp.z, -tmp.y);&lt;br /&gt;#else&lt;br /&gt;   Vec4f tmp(vertex[i].x, vertex[i].y, vertex[i].z, 0);&lt;br /&gt;   tmp = (matrix * tmp) + matrix[3];&lt;br /&gt;   vertex[i] = Vec3f(tmp.x, tmp.y, tmp.z);&lt;br /&gt;#endif&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}  break;&lt;br /&gt;&lt;br /&gt;// ...&lt;br /&gt;&lt;br /&gt;// Loading the face description (index values) trunk&lt;br /&gt;case 0x4120:&lt;br /&gt;{&lt;br /&gt; uint16_t faceCount = 0;&lt;br /&gt; uint16_t* indexArray = nullptr;&lt;br /&gt;&lt;br /&gt; // ...&lt;br /&gt;&lt;br /&gt; if(invertTriangleWinding) {&lt;br /&gt;  for(size_t i=0; i&amp;lt;faceCount*3; i+=3)&lt;br /&gt;   std::swap(indexArray[i], indexArray[i+2]);&lt;br /&gt; }&lt;br /&gt;}  break;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-361030745495762191?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/361030745495762191/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/09/3ds_12.html#comment-form' title='3 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/361030745495762191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/361030745495762191'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/09/3ds_12.html' title='3DS轉換矩陣'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-6070853619325403845</id><published>2008-09-07T22:40:00.000+08:00</published><updated>2008-09-18T16:59:23.473+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='3ds'/><category scheme='http://www.blogger.com/atom/ns#' term='loading'/><title type='text'>3DS 文件格式</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SMPqaahJ32I/AAAAAAAAABE/w8HjU6jv43s/s1600-h/F5E.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SMPqaahJ32I/AAAAAAAAABE/w8HjU6jv43s/s320/F5E.png" alt="" id="BLOGGER_PHOTO_ID_5243292130705268578" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;a href="http://www.meshfactory.com/free_model_05.htm" target="_blank"&gt;於 Mesh Factory 下載模型&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;完成了 3DS 文件的加載器已有多個星期，到今天才有點時間張貼出來。&lt;br /&gt;其實 3DS 已是一個很古老的格式，它的材質參考路徑僅支援 8.3 格式, 是 Dos 年代的產物。但基於這格式的廣泛流傳和結構簡單，因此儘管 &lt;a href="http://www.collada.org/"&gt;Collada&lt;/a&gt; 是大勢所趨，我還是決意使用它作為引擎裡的第一個模型加載器。這樣我就可以快點進行其他方面的進修，待其他模組有了原型以後才加入其他加載器。&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/SMP3ZL1xUzI/AAAAAAAAABM/NxjH9_I3MYk/s1600-h/F5E2.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: right; cursor: pointer; width: 171px; height: 130px;" src="http://3.bp.blogspot.com/_4CNEj03jaok/SMP3ZL1xUzI/AAAAAAAAABM/NxjH9_I3MYk/s320/F5E2.png" alt="" id="BLOGGER_PHOTO_ID_5243306403236500274" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_4CNEj03jaok/SMP5xATBDHI/AAAAAAAAABU/BWPetDwbPEg/s1600-h/bake.PNG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: right; cursor: pointer; width: 166px; height: 130px;" src="http://3.bp.blogspot.com/_4CNEj03jaok/SMP5xATBDHI/AAAAAAAAABU/BWPetDwbPEg/s320/bake.PNG" alt="" id="BLOGGER_PHOTO_ID_5243309011478056050" border="0" /&gt;&lt;/a&gt;我的實作很基本，只支援頂點，索引，標準材質和貼圖；所有的法線得&lt;a href="http://www.devmaster.net/forums/showthread.php?t=414"&gt;自行計算&lt;/a&gt;，雖然我已對 Smoothing Group 加以支援，但有些位置的效果還是顯得不平滑。相信最好的辦法都是直接從 &lt;a href="http://en.wikipedia.org/wiki/Digital_content_creation"&gt;DCC&lt;/a&gt; 工具中讀入法線向量，可惜 3DS 沒有這資訊。另外有好一些物體的位置錯了，似乎純粹讀入頂點位置並不足夠，還以為 3DS 格式用不著轉換矩陣。&lt;br /&gt;&lt;br /&gt;最後我當然把加載器和之前設計好的資源管理和&lt;a href="http://mtlung.blogspot.com/2008/06/resources-loading.html#links"&gt;漸進裝載&lt;/a&gt;模組整合在一起，當中的細節留待日後再作詳述。&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SMP6WVlXSsI/AAAAAAAAABc/cfXaRUtjN14/s1600-h/1.PNG"&gt;&lt;img style="cursor: pointer; width: 176px; height: 137px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SMP6WVlXSsI/AAAAAAAAABc/cfXaRUtjN14/s320/1.PNG" alt="" id="BLOGGER_PHOTO_ID_5243309652847315650" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4CNEj03jaok/SMP6Wpkx77I/AAAAAAAAABk/uD45wSOaKXc/s1600-h/2.PNG"&gt;&lt;img style="cursor: pointer; width: 176px; height: 137px;" src="http://1.bp.blogspot.com/_4CNEj03jaok/SMP6Wpkx77I/AAAAAAAAABk/uD45wSOaKXc/s320/2.PNG" alt="" id="BLOGGER_PHOTO_ID_5243309658213576626" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SMP6XJoebKI/AAAAAAAAAB0/IqNidDKt5ZI/s1600-h/4.PNG"&gt;&lt;img style="cursor: pointer; width: 176px; height: 137px;" src="http://4.bp.blogspot.com/_4CNEj03jaok/SMP6XJoebKI/AAAAAAAAAB0/IqNidDKt5ZI/s320/4.PNG" alt="" id="BLOGGER_PHOTO_ID_5243309666819009698" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-6070853619325403845?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/6070853619325403845/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/09/3ds.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6070853619325403845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6070853619325403845'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/09/3ds.html' title='3DS 文件格式'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_4CNEj03jaok/SMPqaahJ32I/AAAAAAAAABE/w8HjU6jv43s/s72-c/F5E.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-2695465196315331800</id><published>2008-09-03T10:19:00.000+08:00</published><updated>2008-09-05T10:23:12.766+08:00</updated><title type='text'>谷歌瀏覽器 Chrome</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.google.com/chrome/intl/zh-HK/images/dlpage_lg.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px;" src="http://www.google.com/chrome/intl/zh-HK/images/dlpage_lg.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;作為全球資訊網的龍頭大哥谷歌 &lt;a href="http://www.google.com"&gt;Google&lt;/a&gt;, 推出自家的瀏覽器是遲早的事. 這位新晉的瀏覽器名叫 &lt;a href="http://www.google.com/chrome"&gt;Chrome&lt;/a&gt;, 還是 Beta 階段. 它的宣傳標題是 "&lt;span style="font-weight:bold;"&gt;一個方塊，無所不包&lt;/span&gt;", 正合谷歌一貫使用網上平台的理念; 而一個好的瀏覽器將會是這革命的催化劑.&lt;br /&gt;&lt;br /&gt;試用後感覺良好, 介面簡單反應夠快. 而我最喜歡的就是把菜單/按鈕等等介面壓縮成不到80圖素的垂直空間裏, 就連標題棒都省去了.&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_4CNEj03jaok/SL4FVRlD6rI/AAAAAAAAAA8/JPJteP6QzU8/s1600-h/untitled.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_4CNEj03jaok/SL4FVRlD6rI/AAAAAAAAAA8/JPJteP6QzU8/s320/untitled.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5241632879360076466" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;至於內部構和設計理念, 谷歌用了&lt;a href="http://www.google.com/googlebooks/chrome/index.html"&gt;漫畫&lt;/a&gt;形式展示出來. 從中得知谷歌為了避開記憶體洩漏以及安全性的問題, 挑選了一個 Tab, 一個進程 (Process) 的設計. 這有點兒走回頭路的感覺, 但不失為簡單快捷的方案; 何況 Chrome 創建新 Tab 的速度奇快, 沒有半點被創建進程的開銷所拖慢.&lt;br /&gt;&lt;br /&gt;目前 Chrome 和 &lt;a href="http://www.mozilla.com/en-US/firefox/"&gt;Firefox&lt;/a&gt; 相比下還缺少一眾好用的插件, 但相信新一輪瀏覽器之戰又開始了.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-2695465196315331800?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/2695465196315331800/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/09/chrome.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2695465196315331800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2695465196315331800'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/09/chrome.html' title='谷歌瀏覽器 Chrome'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_4CNEj03jaok/SL4FVRlD6rI/AAAAAAAAAA8/JPJteP6QzU8/s72-c/untitled.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-2082981369387627671</id><published>2008-08-08T11:31:00.001+08:00</published><updated>2009-03-18T11:20:55.322+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='simplicity'/><category scheme='http://www.blogger.com/atom/ns#' term='tips'/><title type='text'>C 函數的新發現</title><content type='html'>以往如需要從程式的主迴圈讀取鍵盤的輸入去決定是否退出程式, 我會用另一個執行緒去乎叫 std::cin 或 getchar(), 因為它們都是阻塞 (blocking) 的.&lt;br /&gt;&lt;br /&gt;原來我一直忽略了 &lt;a href="http://www.cprogramming.com/fod/kbhit.html"&gt;kbhit&lt;/a&gt;() 的存在! 有了它, 以上的問題就可簡化為:&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#include &lt;conio.h&gt;&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt; while(true) {&lt;br /&gt;  // Poll the console without blocing, return true if there is&lt;br /&gt;  // a keystroke waiting in the buffer.&lt;br /&gt;  if(kbhit()) {&lt;br /&gt;   if(getchar() == 'q')&lt;br /&gt;    break;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Do something usefull&lt;br /&gt;  // ...&lt;br /&gt; }&lt;br /&gt; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;可惜 kbhit() 不是標準 C 裡的成員, 在若干平台上我們得自行實踐. 以下編碼出於&lt;a href="http://fedoraforum.org/forum/archive/index.php/t-172337.html"&gt;這裡&lt;/a&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#include &amp;lt;sys/select.h&amp;gt;&lt;br /&gt;&lt;br /&gt;int kbhit(void)&lt;br /&gt;{&lt;br /&gt; struct timeval tv;&lt;br /&gt; fd_set read_fd;&lt;br /&gt;&lt;br /&gt; /* Do not wait at all, not even a microsecond */&lt;br /&gt; tv.tv_sec=0;&lt;br /&gt; tv.tv_usec=0;&lt;br /&gt;&lt;br /&gt; /* Must be done first to initialize read_fd */&lt;br /&gt; FD_ZERO(&amp;amp;read_fd);&lt;br /&gt;&lt;br /&gt; /* Makes select() ask if input is ready: 0 is the file descriptor for stdin */&lt;br /&gt; FD_SET(0, &amp;amp;read_fd);&lt;br /&gt;&lt;br /&gt; /* The first parameter is the number of the largest file descriptor to check + 1. */&lt;br /&gt; if(select(1, &amp;amp;read_fd, NULL/*No writes*/, NULL/*No exceptions*/, &amp;amp;tv) == -1)&lt;br /&gt;  return 0; /* An error occured */&lt;br /&gt;&lt;br /&gt; /* read_fd now holds a bit map of files that are&lt;br /&gt;  * readable. We test the entry for the standard&lt;br /&gt;  * input (file 0). */&lt;br /&gt; if(FD_ISSET(0, &amp;amp;read_fd))&lt;br /&gt;  /* Character pending on stdin */&lt;br /&gt;  return 1;&lt;br /&gt;&lt;br /&gt; /* no characters were pending */&lt;br /&gt; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-2082981369387627671?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/2082981369387627671/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/08/c.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2082981369387627671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/2082981369387627671'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/08/c.html' title='C 函數的新發現'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-3980587592597018850</id><published>2008-07-22T10:34:00.001+08:00</published><updated>2009-03-18T11:22:54.536+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='tips'/><title type='text'>共享算術運算子</title><content type='html'>每當實作一些有關數學的類別 (如 Vector, Matrix, Point, Size 等...) 時，加減乘除等運算子都會時常出現。讓我們試試把共同的地方提煉成基類別：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;template&amp;lt;int N&amp;gt;&lt;br /&gt;class Tuple {&lt;br /&gt;public:&lt;br /&gt; Tuple operator+(const Tuple&amp;amp; rhs) const&lt;br /&gt; {&lt;br /&gt;  Tuple result;&lt;br /&gt;  for(int i=0; i&amp;lt;N; ++i)&lt;br /&gt;   result.data[i] = data[i] + rhs.data[i];&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; float data[N];&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class Vec3 : public Tuple&amp;lt;3&amp;gt; {&lt;br /&gt;public:&lt;br /&gt; Vec3(float x, float y, float z) {&lt;br /&gt;  data[0] = x; data[1] = y; data[2] = z;&lt;br /&gt; }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt; Vec3 v1(1, 2, 3);&lt;br /&gt; Vec3 v2(4, 5, 6);&lt;br /&gt; // Compilation error: v1 + v2 is returning Tuple but not Vec3&lt;br /&gt; Vec3 v3 = v1 + v2;&lt;br /&gt; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;大家不用擔心那個回圈會為性能帶來負面影響，編譯器懂得把它優化 (我已在VC2008上證實了這一點)。&lt;br /&gt;但由於運算子的返回型態出了問題，我們作出以下嘗試：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;template&amp;lt;int N, class R&amp;gt;&lt;br /&gt;class Tuple {&lt;br /&gt;public:&lt;br /&gt; R operator+(const Tuple&amp;amp; rhs) const&lt;br /&gt; {&lt;br /&gt;  R result;&lt;br /&gt;  for(int i=0; i&amp;lt;N; ++i)&lt;br /&gt;   result.data[i] = data[i] + rhs.data[i];&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; float data[N];&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class Vec3 : public Tuple&amp;lt;3, Vec3&amp;gt; {&lt;br /&gt;public:&lt;br /&gt; Vec3(float x, float y, float z) {&lt;br /&gt;  data[0] = x; data[1] = y; data[2] = z;&lt;br /&gt; }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt; Vec3 v1(1, 2, 3);&lt;br /&gt; Vec3 v2(4, 5, 6);&lt;br /&gt; Vec3 v3 = v1 + v2;&lt;br /&gt; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;非常好。不過還可以更好哩：&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;template&amp;lt;int N, class R, class U&amp;gt;&lt;br /&gt;class Tuple : public U {&lt;br /&gt;public:&lt;br /&gt; R operator+(const Tuple&amp;amp; rhs) const&lt;br /&gt; {&lt;br /&gt;  R result;&lt;br /&gt;  for(int i=0; i&amp;lt;N; ++i)&lt;br /&gt;   result.data[i] = data[i] + rhs.data[i];&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;struct _Vec3Union {&lt;br /&gt; union {&lt;br /&gt;  struct { float x, y, z; };&lt;br /&gt;  float data[3];&lt;br /&gt; };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class Vec3 : public Tuple&amp;lt;3, Vec3, _Vec3Union&amp;gt; {&lt;br /&gt;public:&lt;br /&gt; Vec3() {}&lt;br /&gt; Vec3(float x_, float y_, float z_) {&lt;br /&gt;  x = x_; y = y_; z = z_;&lt;br /&gt; }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;struct _SizeUnion {&lt;br /&gt; union {&lt;br /&gt;  struct { float width, height; };&lt;br /&gt;  float data[2];&lt;br /&gt; };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class Size : public Tuple&amp;lt;2, Size, _SizeUnion&amp;gt; {&lt;br /&gt;public:&lt;br /&gt; Size() {}&lt;br /&gt; Size(float w, float h) {&lt;br /&gt;  width = w; height = h;&lt;br /&gt; }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt; Vec3 v1(1, 2, 3);&lt;br /&gt; Vec3 v2(4, 5, 6);&lt;br /&gt; Vec3 v3 = v1 + v2;&lt;br /&gt;&lt;br /&gt; Size s1(1, 2);&lt;br /&gt; Size s2(2, 3);&lt;br /&gt; Size s3 = s1 + s2;&lt;br /&gt;&lt;br /&gt; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;這可讓 Vec3 組成的 x, y 和 z 用方便的形式去存取。&lt;br /&gt;&lt;br /&gt;儘管以上的提示未必有多大用途，還望它能加強大家對 C++ 的了解。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-3980587592597018850?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/3980587592597018850/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/07/blog-post_21.html#comment-form' title='4 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3980587592597018850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3980587592597018850'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/07/blog-post_21.html' title='共享算術運算子'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-7267740764861995758</id><published>2008-07-14T14:15:00.001+08:00</published><updated>2009-03-18T11:23:43.051+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='meta programming'/><title type='text'>尼采的神奇 Functor</title><content type='html'>尼采給我的&lt;a href="https://www.blogger.com/comment.g?blogID=2415040221222523714&amp;amp;postID=3441480057438761537"&gt;題目&lt;/a&gt;...不如在此發表答案.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#include &amp;lt;iostream&amp;gt;&lt;br /&gt;&lt;br /&gt;typedef int (*Functor)();&lt;br /&gt;&lt;br /&gt;// Recursive type template that generate a&lt;br /&gt;// compile-time list of Functor using inheritance&lt;br /&gt;template&amp;lt;size_t N&amp;gt; struct Unit : public Unit&amp;lt;N-1&amp;gt; {&lt;br /&gt;  Unit() : mFunctor(&amp;amp;Unit&amp;lt;N&amp;gt;::fun) {}&lt;br /&gt;  static int fun() { return N; }&lt;br /&gt;  Functor mFunctor;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// Partial specialization to end the recursion&lt;br /&gt;template&amp;lt;&amp;gt; struct Unit&amp;lt;0&amp;gt; {&lt;br /&gt;  Unit() : mFunctor(&amp;amp;Unit&amp;lt;0&amp;gt;::fun) {}&lt;br /&gt;  static int fun() { return 0; }&lt;br /&gt;  Functor mFunctor;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// Partial specialization to reduce recursive template complexity&lt;br /&gt;template&amp;lt;&amp;gt; struct Unit&amp;lt;256&amp;gt; : public Unit&amp;lt;255&amp;gt; {&lt;br /&gt;  Unit() : mFunctor(&amp;amp;Unit::fun) {}&lt;br /&gt;  static int fun() { return 256; }&lt;br /&gt;  Functor mFunctor;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// Partial specialization to reduce recursive template complexity&lt;br /&gt;template&amp;lt;&amp;gt; struct Unit&amp;lt;512&amp;gt; : public Unit&amp;lt;511&amp;gt; {&lt;br /&gt;  Unit() : mFunctor(&amp;amp;Unit::fun) {}&lt;br /&gt;  static int fun() { return 512; }&lt;br /&gt;  Functor mFunctor;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// A compile-time maximum count of Functor&lt;br /&gt;static const size_t cMaxN = 768;&lt;br /&gt;&lt;br /&gt;typedef Unit&amp;lt;cMaxN&amp;gt; List;&lt;br /&gt;static const List cList;&lt;br /&gt;&lt;br /&gt;// A function that return a Functor that return i.&lt;br /&gt;// In other words, it selects from a list of compile-time functors&lt;br /&gt;// base on the run-time parameter i.&lt;br /&gt;Functor getFunctor(int i) {&lt;br /&gt;  {  // We have the assumption that Unit&amp;lt;&amp;gt; struct are packed tightly together in cList&lt;br /&gt;    typedef const char* byte_ptr;&lt;br /&gt;    byte_ptr functorAddres1 = byte_ptr(&amp;amp;(static_cast&amp;lt;const Unit&amp;lt;1&amp;gt;*&amp;gt;(&amp;amp;cList)-&amp;gt;mFunctor));&lt;br /&gt;    byte_ptr functorAddres2 = byte_ptr(&amp;amp;(static_cast&amp;lt;const Unit&amp;lt;2&amp;gt;*&amp;gt;(&amp;amp;cList)-&amp;gt;mFunctor));&lt;br /&gt;    (void)functorAddres1;  (void)functorAddres2;&lt;br /&gt;    assert(functorAddres2 - functorAddres1 == sizeof(Functor));&lt;br /&gt;  }&lt;br /&gt;  assert(i &amp;lt;= cMaxN);&lt;br /&gt;  return *((Functor*)(&amp;amp;cList) + i);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;  for(size_t i=0; i&amp;lt;=cMaxN; ++i) {&lt;br /&gt;    Functor f = getFunctor(i);&lt;br /&gt;    std::cout &amp;lt;&amp;lt; f() &amp;lt;&amp;lt; std::endl;&lt;br /&gt;    assert(f() == int(i));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Win少, 我的答案正確嗎?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-7267740764861995758?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/7267740764861995758/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/07/functor.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7267740764861995758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/7267740764861995758'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/07/functor.html' title='尼采的神奇 Functor'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-374453083333208038</id><published>2008-07-14T11:36:00.001+08:00</published><updated>2008-07-14T12:21:29.357+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='simplicity'/><category scheme='http://www.blogger.com/atom/ns#' term='philosophy'/><title type='text'>我認為最好的 Pdf 閱讀器</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_4CNEj03jaok/SHrO4cskk6I/AAAAAAAAAA0/HQM038q98Fo/s1600-h/adobe_icon.PNG"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_4CNEj03jaok/SHrO4cskk6I/AAAAAAAAAA0/HQM038q98Fo/s320/adobe_icon.PNG" alt="" id="BLOGGER_PHOTO_ID_5222714187060646818" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_4CNEj03jaok/SHrN6CjWmbI/AAAAAAAAAAs/muVs3QO9sKs/s1600-h/foxit_icon.png"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_4CNEj03jaok/SHrN6CjWmbI/AAAAAAAAAAs/muVs3QO9sKs/s320/foxit_icon.png" alt="" id="BLOGGER_PHOTO_ID_5222713114890770866" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;不知不覺 &lt;a href="http://www.adobe.com/products/reader/"&gt;Adobe Acrobat Reader&lt;/a&gt; 已經出到第九代了.&lt;br /&gt;在每一次更新的時候, 大家有沒有想過其實可能更有好的選擇呢?&lt;br /&gt;我有一個好推薦, 它就是 &lt;a href="http://www.foxitsoftware.com/pdf/rd_intro.php"&gt;Foxit Reader&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Foxit 的描繪品質和速度與 Acrobat 不分上下, 但記憶體用量和啟動時間就遠勝 Acrobat; 更不用安裝 , 細小可攜 (僅一個 5MB 的可執行檔).&lt;br /&gt;&lt;br /&gt;以下的測試是基於網上的一篇&lt;a href="http://developer.download.nvidia.com/presentations/2006/siggraph/dx10-effects-siggraph-06.pdf"&gt;文章&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt; &lt;table str="" style="border-collapse: collapse; width: 261pt;" border="0" cellpadding="0" cellspacing="0" width="347"&gt;&lt;col style="width: 82pt;" width="109"&gt;  &lt;col style="width: 89pt;" width="118"&gt;  &lt;col style="width: 90pt;" width="120"&gt;  &lt;tbody&gt;&lt;tr style="height: 16.5pt;" height="22"&gt;   &lt;td style="height: 16.5pt; width: 82pt;" height="22" width="109"&gt;&lt;br /&gt;&lt;/td&gt;   &lt;td style="width: 89pt;" width="118"&gt;Adobe Reader 9&lt;/td&gt;   &lt;td style="width: 90pt;" width="120"&gt;Foxit Reader 2.2&lt;/td&gt;  &lt;/tr&gt;  &lt;tr style="height: 16.5pt;" height="22"&gt;   &lt;td style="height: 16.5pt;" height="22"&gt;啟動須時 (秒)&lt;/td&gt;   &lt;td class="xl24" num=""&gt;21&lt;/td&gt;   &lt;td class="xl24" num=""&gt;7&lt;/td&gt;  &lt;/tr&gt;  &lt;tr style="height: 16.5pt;" height="22"&gt;   &lt;td style="height: 16.5pt;" height="22"&gt;記憶體用量&lt;/td&gt;   &lt;td class="xl24"&gt;74MB&lt;/td&gt;   &lt;td class="xl24"&gt;25MB&lt;/td&gt;  &lt;/tr&gt; &lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;縱然 Foxit 缺少某些(許多人都不常用的)功能, 它帶來的眾多好處立即使我把 100 多MB的 Acrobat Reader 從系統中撤除.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-374453083333208038?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/374453083333208038/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/07/pdf.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/374453083333208038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/374453083333208038'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/07/pdf.html' title='我認為最好的 Pdf 閱讀器'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_4CNEj03jaok/SHrO4cskk6I/AAAAAAAAAA0/HQM038q98Fo/s72-c/adobe_icon.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-3441480057438761537</id><published>2008-07-02T17:20:00.000+08:00</published><updated>2008-07-10T15:55:11.858+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='philosophy'/><title type='text'>程式語言效率大比拼</title><content type='html'>有天正在尋找腳本語言的時候無意間發現這個名為 &lt;a href="http://shootout.alioth.debian.org/"&gt;The Computer Language Benchmarks Game&lt;/a&gt; 的網站.&lt;br /&gt;那裡的 benchmark 數據覆蓋多達 76 種語言加編輯器的組合, 19 個試驗程式(大部分還有提供源始碼).&lt;br /&gt;不過, 儘管那裡提供的資料準確無誤, 它所包含的參考價值仍然有限. 問題在於那些試驗程式都過於細小, 以及只有單一的功能. 在一個複雜的應用程式 (如 3D Game Engine), 有&lt;span style="font-weight: bold;"&gt;大量不同種類&lt;/span&gt;的資料需要處理, 因此記憶體的存取很容易成為瓶頸.&lt;br /&gt;一個好例子就是 Java/.Net. 有數據顯示 Java/.Net 可以快過 C/C++, 不過當你走出試驗程式返回現實, 你會感到 Java/.Net &lt;a href="http://www.jelovic.com/articles/why_java_is_slow.htm"&gt;總是慢&lt;/a&gt;半拍的.&lt;br /&gt;&lt;br /&gt;其實那網站的第一段就表明了:&lt;br /&gt;&lt;h5&gt;&lt;/h5&gt;&lt;span style="font-weight: bold;"&gt;Benchmarking programming languages?&lt;/span&gt;&lt;br /&gt;How can we benchmark a programming language?&lt;br /&gt;We can't - we benchmark programming language implementations.&lt;br /&gt;&lt;br /&gt;How can we benchmark language implementations?&lt;br /&gt;&lt;span style="font-weight: bold;"&gt; We can't - we measure particular programs!&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-3441480057438761537?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/3441480057438761537/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/07/blog-post.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3441480057438761537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/3441480057438761537'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/07/blog-post.html' title='程式語言效率大比拼'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-4282429644768106755</id><published>2008-06-18T10:43:00.001+08:00</published><updated>2008-06-19T11:38:59.844+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='thread'/><category scheme='http://www.blogger.com/atom/ns#' term='simplicity'/><category scheme='http://www.blogger.com/atom/ns#' term='loading'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='progressive'/><title type='text'>Resources loading</title><content type='html'>最近完成了漸進裝載系統 (Progressive resource loading) 的設計與實作.&lt;br /&gt;現階段能夠以多緒形式載入 Jpeg 和 Png&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SFh2oBsz4bI/AAAAAAAAAAM/2XdAewvog9Q/s1600-h/cube1.png"&gt;&lt;img style="width: 228px; height: 228px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SFh2oBsz4bI/AAAAAAAAAAM/2XdAewvog9Q/s320/cube1.png" alt="" id="BLOGGER_PHOTO_ID_5213046998704578994" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SFh2odWa18I/AAAAAAAAAAU/sSW3pDbs9jo/s1600-h/cube2.png"&gt;&lt;img style="width: 228px; height: 228px;" src="http://2.bp.blogspot.com/_4CNEj03jaok/SFh2odWa18I/AAAAAAAAAAU/sSW3pDbs9jo/s320/cube2.png" alt="" id="BLOGGER_PHOTO_ID_5213047006126856130" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;要達到多緒, 首要分開載入和使用中的資料.&lt;br /&gt;函式 load() 會把 istream 中的資料解碼到 loader 裏的私有緩衝區之中, 這時候 LoadingState 為 Loading.&lt;br /&gt;當一定數量的資料已解碼而又可以顯示出來, load() 會返回 LoadingState = PartialLoaded, 這時候你可以乎叫 commit() 把 loader 中的緩衝抄到 resource 去.&lt;br /&gt;請注意 load(), commit() 和 getLoadingState() 好有可能在不同的執行緒中執行, 因此 loader 裏的緩衝區和 LoadingState 成員都要用 Mutex 來保護.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_4CNEj03jaok/SFh2ojL1dVI/AAAAAAAAAAc/q9U-04PZxlc/s1600-h/resource_class_diag.png"&gt;&lt;img src="http://2.bp.blogspot.com/_4CNEj03jaok/SFh2ojL1dVI/AAAAAAAAAAc/q9U-04PZxlc/s320/resource_class_diag.png" alt="" id="BLOGGER_PHOTO_ID_5213047007693075794" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_4CNEj03jaok/SFh2or0rt_I/AAAAAAAAAAk/nIotzTyrBkg/s1600-h/resource_sequence_diag.png"&gt;&lt;img src="http://4.bp.blogspot.com/_4CNEj03jaok/SFh2or0rt_I/AAAAAAAAAAk/nIotzTyrBkg/s320/resource_sequence_diag.png" alt="" id="BLOGGER_PHOTO_ID_5213047010011887602" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;有一點值得討論的就是 load() 這函式.&lt;br /&gt;現在的設計須要用者不停地乎叫 load() 直到它返回 Loaded 或 Aborted. 但我曾經考慮過使用事件 (Event) 來通知用者現在的 LoadingState 從而只需乎叫 load() 一次. 接著當我試圖實作 cancel() 這函式時, 問題出現了; 我得建造另一個事件來控制 load() 內部的循環何時跳出.&lt;br /&gt;實在不好, 複雜跳進來了...還要處理令人頭痛的多緒同步哩! 想起 &lt;a href="http://www.xmlpull.org/"&gt;Pull Xml&lt;/a&gt; 帶給我的啟示, 原來只要把 load() 內的循環交給用者來定義, 一切都簡單得多.&lt;br /&gt;再細心的想, 如果用者希望一幅圖像載入了第一個漸進後便去開始載入另一幅圖像的話, 用事件的作法是行不通的 (或變得異常複雜). 所以這個新的設計不止簡單, 還非常靈活. 我又一次體會到什麼是 &lt;a href="http://gettingreal.37signals.com/ch10_Less_Software.php"&gt;&lt;span style="font-weight: bold;"&gt;Less is more&lt;/span&gt;&lt;/a&gt;, &lt;a href="http://bradapp.blogspot.com/2005/02/there-is-no-code-that-is-more-flexible.html"&gt;&lt;span style="font-weight: bold;"&gt;There is No CODE that is more flexible than NO Code!&lt;/span&gt;&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-4282429644768106755?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/4282429644768106755/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/06/resources-loading.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/4282429644768106755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/4282429644768106755'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/06/resources-loading.html' title='Resources loading'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_4CNEj03jaok/SFh2oBsz4bI/AAAAAAAAAAM/2XdAewvog9Q/s72-c/cube1.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-6567135355638383624</id><published>2008-06-11T09:28:00.001+08:00</published><updated>2009-03-18T11:26:58.919+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='tips'/><category scheme='http://www.blogger.com/atom/ns#' term='philosophy'/><title type='text'>C++ friend class</title><content type='html'>有些時候我們會利用 C++ 中的 friend 關鍵字來設定類別的好友.&lt;br /&gt;但可惜一個 friend 宣告只會對一個類別產生作用:&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;class Texture {&lt;br /&gt;public:&lt;br /&gt; friend class IResourceLoader;&lt;br /&gt; uint width() const { return mWidth; }&lt;br /&gt; uint height() const { return mHeight; }&lt;br /&gt; private:&lt;br /&gt; uint mWidth;&lt;br /&gt; uint mHeight;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class IResourceLoader {};&lt;br /&gt;&lt;br /&gt;class JpegLoader : public IResourceLoader {&lt;br /&gt; void load() {&lt;br /&gt;  // ...&lt;br /&gt;  mTexture.mWidth = 128;  // Compiler error!!&lt;br /&gt; }&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;以上的 Texture 類別有成員 mWidth 和 mHeight, 它們都只應由 IResourceLoader 的實作類別所能更改. 幸好有 Inner class 可以幫到我們:&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;// Texture.h&lt;br /&gt;class Texture {  &lt;br /&gt;public:  &lt;br /&gt; friend class IResourceLoader;  &lt;br /&gt; uint width() const { return mWidth; }  &lt;br /&gt; uint height() const { return mHeight; }  &lt;br /&gt;&lt;br /&gt; // We declare a templated inner class here (but not defined yet)&lt;br /&gt; template&lt;class&gt; class PrivateAccessor;&lt;br /&gt;&lt;br /&gt; private:  &lt;br /&gt; uint mWidth;  &lt;br /&gt; uint mHeight;  &lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class IResourceLoader {};  &lt;br /&gt;&lt;br /&gt;// JpegLoader.cpp&lt;br /&gt;// Define Texture::PrivateAccessor here, access what ever you want&lt;br /&gt;template&lt;&gt;&lt;br /&gt;class Texture::PrivateAccessor&lt;jpegloader&gt; {&lt;br /&gt;public:&lt;br /&gt; static size_t&amp;amp; width(Texture&amp;amp; texture) {&lt;br /&gt;  return texture.mWidth;&lt;br /&gt; }&lt;br /&gt; static size_t&amp;amp; height(Texture&amp;amp; texture) {&lt;br /&gt;  return texture.mHeight;&lt;br /&gt; }&lt;br /&gt;};&lt;br /&gt;typedef Texture::PrivateAccessor&lt;jpegloader&gt; Accessor;&lt;br /&gt;&lt;br /&gt;void JpegLoader::load() {&lt;br /&gt; // ...  &lt;br /&gt; Accessor::width(mTexture) = 128; // Ok! no problem&lt;br /&gt; Accessor::height(mTexture) = 128;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;你可能會認為這樣做等同把所有成員變為公開 (public), 事實上這個方案的精神在於迫使程序員清楚自己正在做什麼, 而不是錯誤地更改了變數.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-6567135355638383624?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/6567135355638383624/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/06/inlcude-int-main-stdcout-hello-worldn.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6567135355638383624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6567135355638383624'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/06/inlcude-int-main-stdcout-hello-worldn.html' title='C++ friend class'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-6816374144734382219</id><published>2008-06-11T09:23:00.000+08:00</published><updated>2008-06-16T14:21:48.282+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='syntax highlight'/><title type='text'>Syntax highlighting</title><content type='html'>要貼原碼第一件事當然是設置"&lt;b&gt;語法高亮度顯示&lt;/b&gt;" (&lt;a href="http://en.wikipedia.org/wiki/Syntax_highlight"&gt;Syntax highlight&lt;/a&gt;)&lt;br /&gt;&lt;a href="http://code.google.com/p/syntaxhighlighter/"&gt;Syntaxhighlighter&lt;/a&gt; 是一個純粹用客戶端 Java Script 來達到效果.&lt;br /&gt;&lt;br /&gt;還有一些文章 &lt;a href="http://developertips.blogspot.com/2007/08/syntaxhighlighter-on-blogger.html"&gt;1&lt;/a&gt;, &lt;a href="http://morten.lyhr.dk/2007/12/how-to-get-syntax-highlighting-in.html"&gt;2&lt;/a&gt; 有更好的指示.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-6816374144734382219?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/6816374144734382219/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/06/syntax-highlighting.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6816374144734382219'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/6816374144734382219'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/06/syntax-highlighting.html' title='Syntax highlighting'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2415040221222523714.post-8875100054072885902</id><published>2008-06-06T09:34:00.000+08:00</published><updated>2008-06-10T14:51:02.902+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='philosophy'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><title type='text'>學習, 再學習</title><content type='html'>經過三年的遊戲引擎開發後, 縱然我從中已學到了許多; 但還要學的更多, 無窮無盡的多.&lt;br /&gt;僅僅 C++ 這令人又愛又恨的東西我已學了十年 (&lt;a href="http://norvig.com/21-days.html"&gt;Teach yourself programming in ten years&lt;/a&gt;).&lt;br /&gt;那其實是一件好事, 我就是喜歡學習的過程.&lt;br /&gt;&lt;br /&gt;華而不實的東西我已做過不少 (如 &lt;a href="http://exmat.sf.net/"&gt;Exmat&lt;/a&gt; ).&lt;br /&gt;現在我正集中焦點學習怎樣去設計簡單清晰的軟件.&lt;br /&gt;&lt;br /&gt;而這裡正是用來把這過程記錄下來, 希望大家多多指教.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2415040221222523714-8875100054072885902?l=mtlung.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mtlung.blogspot.com/feeds/8875100054072885902/comments/default' title='張貼意見'/><link rel='replies' type='text/html' href='http://mtlung.blogspot.com/2008/06/blog-post.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/8875100054072885902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2415040221222523714/posts/default/8875100054072885902'/><link rel='alternate' type='text/html' href='http://mtlung.blogspot.com/2008/06/blog-post.html' title='學習, 再學習'/><author><name>Ricky</name><uri>http://www.blogger.com/profile/11234732741497411647</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>
