2009年6月24日 星期三

淺談八零年代三大漫畫續作

九零年代的代表漫畫,大家都會舉七龍珠、悠遊白書做例子。而在八零年代,也是有代表性的作品,其中,曉!男塾、蒼天之拳跟天使心這三部作品,分別是魁!男塾、北斗神拳和城市獵人的續作。其實這幾位漫畫家應該都賺飽了,想要在這個時間點推出作品,不知道是錢花光了,還是太無聊,本人不得而知XD。不過來聊聊這三部作品吧。

曉!男塾,我從第一集就開始追了,故事男主角是劍獅子丸,上一代劍桃太郎的兒子,不過宮下亞喜羅大概太久沒作品了,失去了熱血要素,跟前作相比實在遜色太多,不過人帥真好在魁!男塾也充分展現,看看那飛燕不知道該死幾次了,每次都能復活過來,真不愧是中國四千年醫術。至於唬爛能力,也是不如前作,不過到了二十集之後,整個唬爛的能力有回來一點,可喜可樂。

蒼天之拳,大概是三部續作之中,我最喜歡的一部,故事背景在北斗神拳之前,算是前傳,霞拳志郎是北斗神拳的主角拳志郎(二人的名字相同,或叫作拳四郎、健次郞)的伯父,相較於北斗神拳走的悲壯路線,蒼天之拳帶有一點詼諧的成份在裡面,不過廉頗老矣,原哲夫因為眼睛的關係,作畫品質大不如前。

天使心,根據北条司自己的說法,這不算是City Hunter續作,當作平行世界來看。不過由於City Hunter太過於成功,沒人會把他的話當真XD。城市獵人會紅的原因十分簡單,他就是要畫一個英雄,而他成功了。而天使心描述的是親情方面,老實說這並不合我胃口。

簡單寫一下這三部漫畫的近況,由於三部都未完結,還不能概觀論定期最後評價。

2009年6月18日 星期四

所謂的愛國精神

到哪裡都可以展現!

使用Android應用程式選單

在一般的視窗程式中,選單是一個很方便的元件,因為它避免過多的按鈕擠在同一個視窗中。對於螢幕較小的手機平台來說,選單功能更是重要。本篇文章將會示範如何應用Android平台的選單元件。







 

要將選單加入程式,首先就得了解Menu這個類別有下列兩個重要的method。

  • public boolean onCreateOptionsMenu(Menu)
  • public boolean onOptionsItemSelected(MenuItem)

一個是程式啟動時,負責將選單加到程式裡。另一個則是當使用者按下選單上的按鈕時就會被呼叫,後續的處理都是放在這個method裡。

了解這些後,就開始動手實作吧!

 

照慣例,先開一個新專案。

2009-06-16_232540

 

然後在res資料夾裡,新建一個menu資料夾,用來放設計好的選單。

2009-06-16_233009

接下來新增一個xml檔案,用來設計選單的外觀。

2009-06-16_233043

新增之後,打開xml檔案,就會出現一個編輯視窗,現在來新增兩個按鈕吧。

2009-06-16_233146

按鈕Add按鈕,就會跳出一個對話方塊,這時候點選Item並按下確認鈕。

2009-06-16_233220

然後就會看到視窗上多了一個item,點選之後就會顯示相關的屬性,接下來就修改Id跟Title這兩個屬性。

2009-06-16_233300 2009-06-16_234011

之後照前面的方法再新增一個Item。

2009-06-16_233352

 

編輯完選單之後,就開始動手寫程式吧。在這之前,請記得加入下列package。

2009-06-16_233744

接下來新增前面所提到的兩個method,首先是onCreateOptionsMenu。

2009-06-16_233831

這部份很簡單,就是將編輯好的menu加入到程式中。

接下來新增這個package,後面會用到。

2009-06-16_234217

接下來是onOptionsItemSelected,從字面上來看,就知道這是用來處理按下選單按鈕後的事情。

2009-06-16_234244 

要判斷使用者按下的是哪個按鈕,只要利用MenuItem的getItemId就可以了。

 

接下來執行程式,按下模擬器上的MENU硬體按鍵,螢幕上就會跳出選單。

2009-06-16_234405

2009-06-16_234549

2009年6月9日 星期二

影子傳說之Planar Shadow

玩3D遊戲時,影子的真實度,往往是讓遊戲更顯真實的方式之一。
而目前的主流是Shadow Map,Shadow Volume跟Planar Shadow已經逐漸少用。
從簡單的方法開始,逐步介紹影子的兩三事。

Planar Shadow的原理很簡單,三度空間上的一個點v,空間中的一盞光源,方向為L,由v為起點,做出一條射線,方向為光源方向,會與平面P相交於一點v’,而這點就是光線對這點在平面上的投影,而我們要做的就是把點v’算出來。

為了簡單說明原理,簡化了很多東西,光源是Directional Light,只有Diffuse Color,只有一個平面跟一個物體。

由於v’在射線上,且v’在平面上,所以我們可以導出以下公式。

 
 
由這公式我們可以算出


 
這樣子我們就能把所有頂點的投射點算出來了。
不過為了配合電腦硬體的設計,我們使用一個Homogeneous Matrix來幫助我們算出每個投射點。

用一個簡單的程式來示範Planar Shadow︰ (由於OpenGL是Column Major,跟我們習慣的書寫方式不同,所以我們需要他的轉置矩陣。)

 

#include "glut.h"
#include <gl/GL.h>
#include <gl/GLU.h>
// plane equation
GLfloat planeequation[4] = {0, 1, 0, 0};
// light0 declaration
GLfloat lightdirection[4] = {0.577, 0.577, 0.577, 0};
GLfloat lightDiffuse[4] = {1.0, 1.0, 1.0, 1.0};
//
GLfloat g_planeDiffuse[4] = {1.0, 1.0, 1.0, 1.0};
GLfloat g_objectDiffuse[4] = {0.0, 1.0, 0.0, 1.0};

GLfloat shadow_color[4] = { 0.3f, 0.3f, 0.3f, 1.0f};
GLfloat shadow_color_matrix[4][4];
void generate_shadow_color_matrix(GLfloat matrix[4][4],
GLfloat light[4], GLfloat plane[4])
{
GLfloat dot = 0;
for (int i = 0; i < 4; i++) dot += light[i] * plane[i];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (i == j) {
matrix[i][j] = dot - plane[i] * light[j];
} else {
matrix[i][j] = -plane[i] * light[j];
}
}
void DrawObject()
{
glBegin(GL_QUADS);
glNormal3f(0, 1, 0);
glVertex3f(2.5, 0, -2.5);
glVertex3f(-2.5, 0, -2.5);
glVertex3f(-2.5, 0, 2.5);
glVertex3f(2.5, 0, 2.5);
glEnd();
}
void Display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glMaterialfv(GL_FRONT, GL_DIFFUSE, (float *) &g_planeDiffuse);

glBegin(GL_QUADS);
glNormal3f(0, 1, 0);
glVertex3f(5, 0, -5);
glVertex3f(-5, 0, -5);
glVertex3f(-5, 0, 5);
glVertex3f(5, 0, 5);
glEnd();
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
glPushMatrix();
glMultMatrixf((GLfloat*) shadow_color_matrix);
glTranslatef(0, 2.5, 0);
glColor3fv(shadow_color);
DrawObject();
glPopMatrix();
glDisable(GL_BLEND);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glMaterialfv(GL_FRONT, GL_DIFFUSE, (float *) &g_objectDiffuse);
glPushMatrix();
glTranslatef(0, 2.5, 0);
DrawObject();
glPopMatrix();
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);

glutSwapBuffers();
}
void Reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
gluPerspective(60.0f, (GLfloat)width / (GLfloat)height, 0.5f, 50.0f);
}
void Keyboard(unsigned char key, int x, int y)
{
switch(key)
{
case 27:
exit(0);
break;
}
}
void init()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, 10, 0, 0, 0, 0, 0, 0, 1);
glLightfv(GL_LIGHT0, GL_POSITION, lightdirection);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
generate_shadow_color_matrix(shadow_color_matrix, lightdirection, planeequation);
}
int main()
{
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowPosition(100,100);
glutInitWindowSize(640, 640);
glutCreateWindow("Planar shadow_color");
glutDisplayFunc(Display);
glutReshapeFunc(Reshape);
glutKeyboardFunc(Keyboard);
init();
glutMainLoop();
}


優點嘛,不需要額外硬體支援。

缺點倒是不少,除了Shadow Map也會遇到的Z-Fighting問題,還有組合爆炸的問題﹔也就是說,假設有L個光源,M個平面,你上面的陰影繪圖動作就必須做L*M次;最大的問題是,你必需要有個平面,如果是球面,曲面是無效的。


如有更深入的研究,可翻閱參考文件。



參考文件︰

1. 即時 3D 繪圖的陰影效果


2. Shadow Projection in OpenGL

2009年6月4日 星期四

第一次寫Shader就上手

我還真是什麼都略懂一點,大概可以當金城武了吧。
雖然我研究所念Image & Graphics出來的,不過工作跟這完全無關,都忘光了XD。
這篇是拿來恢復信心用的。

這篇不是專業教學文,不會告訴你整個Graphics Pipeline的流程。請自行上網搜尋解答。
這篇說的指是冰山一角,整個Pipeline的中間地帶Pixel Shader,主要負責上色和處理其他屬性。

而我們用來開發的環境,也不需要Visual Studio 或 DirectX SDK,我們只需要一個軟體。
Media Player Classic 你沒看錯,就是播放軟體,我以中文版作為示範,開發一個Shader來玩玩。
要寫Shader,第一個要件就是你的顯卡至少要支援DirectX 9,至於Pixel Shader的演進史可以參照這裡

接著我們需要把MPC當中的Shader功能打開,功能表檢視->選項->播放->輸出的畫面如下所示︰

 

 

 

 

 

 

 

在選項中,需要將所有VMR9DirectX 9、 使用3d 文字表面和成像視訊圈選起來。按確定進行下一步動作。

接著同樣在檢視->Shader編輯下,開始我們的Shader之旅。

底下部份是讓你寫程式碼的,當你寫好之後,在右上方選擇Shader適用版本,然後左上方命名你要命名的Shader Name,一旦命名完按下Enter,此時就會幫你Compile程式碼,一旦成功了會出現D3DXCompileShader succeeded的字樣。

在此我們寫一個OnlyRed的例子︰

sampler s0 : register(s0);
float4 main(float2 tex : TEXCOORD0) : COLOR
{
    float4 color = tex2D(s0, tex);
    return float4(color.r, 0, 0, 1);
}

接著播放影片,在播放->Shaders裡挑選剛剛編譯過的程式碼。

以下為程式執行結果︰

感謝東方不敗擔任男主角。

2009年6月1日 星期一

Singleton in windows modules

Singleton應該是GOF書中的Design Pattern中,最常被使用的Pattern之一。

而在實做上,有許多不同的變化。為了彰顯此次的主題,採用最簡單的方法來表示(Meyer's Singleton)。


class Singleton {

private:
Singleton() { cout << "Constructor" << endl; }
~Singleton() { cout << "Destructor" << endl; }
public:
static Singleton& GetSingleton() {
static Singleton ms_singleton;
return ms_singleton;
}
void Func() { cout << "Func invoked" << endl; }
};

主程式的部份︰

#include "Singleton.h"
int main()
{
Singleton::GetSingleton().Func();
return 0;
}


結果應該是︰
Constructor
Func invoked
Destructor
一切看起來都很美好。

不過當程式逐漸複雜化,需要將程式切成幾個Modules之後(Dynamic Linking Library、非Static Linking Library),事情有了微妙的變化。
我們在Dynamic Linking Library輸出一個Function,他的實做就是
void Func()
{

Singleton::GetSingleton().Func();
}


我們的主程式改寫成這樣
int main()
{
Singleton::GetSingleton().Func();
Func();
return 0;
}

以下是執行結果︰
Constructor
Func invoked
Constructor
Func invoked
Destructor
Destructor
這結果跟Singleton的定義背道而馳,Singleton的原則就是只有一個實體。

會造成這樣的原因是,由於Modules都能看到完整的Singleton的implementation,在Modules之間並不知道已經有某個 Module已經建立了Singleton,於是在自己的Module間重新建立一個Singleton,於是就造成這種現象。

對於這個問題,微軟也提供了一套解決方案,就是統一使用某Module的Singleton。
#ifdef SINGLETON_EXPORTS
    #define _DLLExport __declspec(dllexport)
#else
    #define _DLLExport __declspec(dllimport)
#endif
class Singleton {
private:
Singleton() { cout << "Constructor" << endl; }
~Singleton() { cout << "Destructor" << endl; }
public:
static _DLLExport Singleton& GetSingleton() {
static Singleton ms_singleton;
return ms_singleton;
}
void Func() { cout << "Func invoked" << endl; }
};

然後在提供Singleton Object的Module中加入 MARCO SINGLETON_EXPORTS,一切大功告成。

結果就如我們所預期了。
Constructor
Func invoked
Func invoked
Destructor

我果然走清新、健康、專業路線啊。

利用HttpWebRequest取得網頁資料的某部份

取得網頁資料的某部份

有時候我們想要取得某一個網頁的某一個部份的值
.NET裡面就有很好用的Class {System.Net} 裡的 HttpWebRequest

所以寫個簡單範例來看一下

Function GetPage(ByVal txtURL As String) As String

Dim strResponse As String
Dim strState As String
Dim strServer As String
Dim strTitle As String

Dim Result As String

Dim Request As HttpWebRequest
Dim Response As HttpWebResponse

Request = WebRequest.Create(txtURL)

Response = Request.GetResponse

Dim Answer As Stream
Dim SR As StreamReader

Answer = Response.GetResponseStream

SR = New StreamReader(Answer, Text.Encoding.UTF8)

strResponse = SR.ReadToEnd()

strState = Response.StatusCode.ToString

strServer = Response.Server

SR.Close()

If strState = "OK" Then
If strResponse.Length > 0 And strResponse.IndexOf("<title>") > 0 Then
strTitle = strResponse.Substring(strResponse.IndexOf("<title>") + 7, strResponse.IndexOf("</title>") - strResponse.IndexOf("<title>") - 7)
Result = strServer & "-" & strState & "-" & strTitle
Else
Result = strServer & "-" & strState & "-" & " XXXX "
End If
Else
Result = strServer & "-" & strState & "-" & " XXXX "
End If

Return Result

End Function

Protected Sub BTN_Search_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BTN_Search.Click

LAB_Result.Text = GetPage(TB_URL.Text)

End Sub

結果顯示 -- WebServer名稱, 狀態, 網頁的Title

 

我們搬家了

新部落格在http://b-plurkers.com/