Programming pipeline & shading language
大家好,今天想給大家介紹一下可編程渲染管線和著色器語(yǔ)言的相關(guān)基礎(chǔ)知識(shí),使想上手SHADER編程的童鞋們可以快速揭開SHADER語(yǔ)言的神秘面紗
由于時(shí)間有限,我決定只講三個(gè)主要方面的內(nèi)容,其過(guò)程中肯定會(huì)有不詳細(xì)之處,還請(qǐng)見(jiàn)諒,就算是拋磚引玉,給大家一個(gè)簡(jiǎn)單的入門引路。
本章內(nèi)容總共分為三個(gè)部分 一、3D渲染管線工作流程 二、可編程管線 三、著色器語(yǔ)言
3D渲染管線作為整個(gè)工作流程的基礎(chǔ),是不可或缺的基本知識(shí)。因此,作一定的講解是有必要的。 但作為一個(gè)回顧內(nèi)容,就不會(huì)對(duì)具體的內(nèi)容進(jìn)行講解,比如如何進(jìn)行坐標(biāo)系變換,如何進(jìn)行光柵化等等。 我們僅關(guān)注的是整個(gè)工作的過(guò)程。
甚至,我們更關(guān)心的不是整個(gè)工作過(guò)程中的細(xì)節(jié),而是我們所必須要關(guān)注的幾大流程。 如下圖
數(shù)據(jù)填充
當(dāng)我們想實(shí)現(xiàn)一次渲染效果的時(shí)候,數(shù)據(jù)的提交(填充)是不可缺少的。 因此,工作流程的第一步就是要處理輸入的數(shù)據(jù)。
而我們最直接的接觸3D渲染流程的時(shí)機(jī),也就是數(shù)據(jù)填充時(shí),更確切的說(shuō),就是那一堆set數(shù)據(jù)的API。
數(shù)據(jù)填充允許我們提交我們想要的數(shù)據(jù),比如頂點(diǎn)數(shù)據(jù)(如位置,法線,顏色,紋理坐標(biāo)等)。常量(如世界矩陣,觀察矩陣,投影矩陣,紋理因子等等)。
變換&頂點(diǎn)光照
在這個(gè)階段,頂點(diǎn)會(huì)經(jīng)過(guò)世界變換,觀察變換,投影變換。 通常情況下,在頂點(diǎn)經(jīng)過(guò)觀察變換后,便開始做一些光照計(jì)算。 這一階段也是可編程管線所提供的頂點(diǎn)處理階段。也就是說(shuō),我們可以通過(guò)著色器程序來(lái)控制這一階段的結(jié)果。 在著色器程序中,我們可以任意地處理想要的數(shù)據(jù),比如進(jìn)行紋理坐標(biāo)縮放,旋轉(zhuǎn),隨機(jī)偏移等等。
裁剪&光柵化
當(dāng)經(jīng)過(guò)坐標(biāo)變換和光照后,頂點(diǎn)已經(jīng)被投影為2D坐標(biāo)+深度信息。 一些不可見(jiàn)的頂點(diǎn)會(huì)被裁剪掉,比如那些處于背面的點(diǎn)。 同時(shí),剩下的頂點(diǎn)會(huì)被插值計(jì)算,以形成由像素構(gòu)成的圖元。 所有的信息都會(huì)被插值,如紋理坐標(biāo),法線,顏色等。
像素處理
像素處理階段是一個(gè)最耗時(shí),但是也是最能夠使你的渲染效果品質(zhì)更高的地方,像素最終的樣子會(huì)在此決定,你可以進(jìn)行紋理映射,紋理混合,模糊,擴(kuò)散等效果。
這也是可編程管線中可以使用SHADER控制的另一個(gè)處理過(guò)程,
像素的一些額外處理和輸出
當(dāng)像素經(jīng)過(guò)像素處理階段后,并不能都有機(jī)會(huì)輸出到屏幕上,因?yàn)樗鼈冞€要經(jīng)過(guò)深度(也有一些比較優(yōu)化的渲染管線將深度測(cè)試提到了像素處理前)和模板測(cè)試,ALPHA測(cè)試,經(jīng)過(guò)這些測(cè)試后, 還要進(jìn)行一次ALPHA混合,這次與目標(biāo)緩沖區(qū)的混合,就能夠?qū)崿F(xiàn)半透明效果。 虛擬世界中的五光十色就是因?yàn)檫@個(gè)半透明效果而生動(dòng)。
-------------------------------------------------------
正如上面所說(shuō),在3D渲染流程中,我們能夠用著色器語(yǔ)言控制的就是“頂點(diǎn)變換和光照” 以及 “像素處理”階段。 在我們講如何控制之前我大概介紹一下GPU中用于處理著色器的最基本的幫手 - 寄存器。
GPU中的寄存器與CPU中的普通寄存器有一點(diǎn)不同, GPU中的每一個(gè)寄存器都是一個(gè)四維向量寄存器,即一個(gè)寄存器擁有4個(gè)浮點(diǎn)分量。 通常我們用
(x,y,z,w)或者(r,g,b,a)來(lái)表示。
輸入寄存器
輸入寄存器是GPU用來(lái)接受數(shù)據(jù)的寄存器,當(dāng)我們將渲染數(shù)據(jù)填充到GPU時(shí),其實(shí)就是將這些數(shù)據(jù)填充到這些輸入寄存器上。
比如,當(dāng)我們將一個(gè)頂點(diǎn)的位置和法線提交后,GPU在處理這個(gè)頂點(diǎn)時(shí),其對(duì)應(yīng)的寄存器就會(huì)擁有這個(gè)頂點(diǎn)相應(yīng)的值。
頂點(diǎn)處理階段和像素處理階段用到的輸入寄存器是不同的。輸入寄存器決定了對(duì)應(yīng)的處理階段能夠做的事情。
比如,我們提交了一個(gè)三角形的頂點(diǎn)和紋理坐標(biāo)信息,并且我們提交了一張紋理,用來(lái)對(duì)這個(gè)三角形做紋理映射。 但是,我們是不能在頂點(diǎn)處理階段就對(duì)其紋理做處理的。 因?yàn)槲覀儾荒茉陧旤c(diǎn)處理時(shí)訪問(wèn)紋理數(shù)據(jù)(如果真要這樣,那就只能夠使用頂點(diǎn)紋理了,這個(gè)內(nèi)容超出了本次介紹的范圍,固不再多說(shuō))。
常量寄存器
常量寄存器用來(lái)向著色器傳遞我們所需要控制的常量信息,比如,世界矩陣,觀察矩陣,投影矩陣,紋理矩陣等。 以及我們可以設(shè)置一些值,比如當(dāng)前時(shí)間,用來(lái)實(shí)時(shí)偏移一個(gè)頂點(diǎn)的紋理坐標(biāo),使其紋理呈移動(dòng)的效果。 又或者通過(guò)這個(gè)值,動(dòng)態(tài)改變頂點(diǎn)的位置,使其出現(xiàn)波動(dòng)效果。 這些就是常量寄存器可以干的事。
同樣,常量頂點(diǎn)處理階段和像素處理階段使用的常量寄存器也是不同的。不過(guò),這種情況在SM 4.0以后得到改善,并且有一個(gè)趨勢(shì),就是頂點(diǎn)處理和像素處理階段界線不再那么明顯。 他們可以共用寄存器,共用一些緩存。 但在你沒(méi)有完全掌握它們的特點(diǎn)以前,還是老老實(shí)實(shí)記住這個(gè)特性吧。
臨時(shí)寄存器
臨時(shí)寄存器使我們能夠在著色器處理的過(guò)程中存放一些臨時(shí)的值,若你是用高級(jí)著色語(yǔ)言編寫著色程序,那你是感覺(jué)不到臨時(shí)寄存器存在的,因?yàn)槟銉H僅是聲明了一個(gè)臨時(shí)變量。 但確實(shí),這就是臨時(shí)寄存器的功勞。 它才是真正的幕后黑手。
紋理采樣寄存器
紋理寄存器用于存放你所提交的紋理,并且提供紋理采樣功能。 如臨近點(diǎn)采樣,雙線性采樣,三線性采樣等。 這些都肯定你的指示來(lái)做相應(yīng)的工作。 它主要是輔助你完成紋理映射工作。
輸出寄存器
輸出寄存器就是你著色程序能夠輸出的內(nèi)容,輸出內(nèi)容通過(guò)輸出寄存器傳遞出來(lái)。 頂點(diǎn)處理程序的輸出有兩種, 一種是輸出到幀緩沖,另一種是輸出給像素處理程序,最典型的就是紋理坐標(biāo)數(shù)據(jù), 當(dāng)頂點(diǎn)處理程序拿到輸入寄存器傳遞過(guò)來(lái)的紋理坐標(biāo)值后,經(jīng)過(guò)一些處理,又輸出。 而真正需要使用這個(gè)信息的,就是像素處理程序。
常見(jiàn)的有 位置,紋理坐標(biāo),顏色等。
-----------------------------------
下面,我們來(lái)看一下著色器語(yǔ)言吧。
著色器程序就如上面講的那樣,分為了頂點(diǎn)著色程序和像素著色程序。 你可能會(huì)發(fā)現(xiàn),這里多了一個(gè)幾何著色程序,這個(gè)是后來(lái)新加入的兄弟,傳統(tǒng)的著色程序不能增加刪除頂點(diǎn)。 但是,它可以。 有興趣的童鞋中以繼續(xù)去了解。
著色器語(yǔ)言有高級(jí)語(yǔ)言和低級(jí)語(yǔ)言兩種。 低級(jí)語(yǔ)言采用的是匯編那種助記符方式。
如 dp4 r0,v0,c0 這樣的,表示將v0,c0點(diǎn)乘,并放入臨時(shí)寄存器r0中。
而高級(jí)語(yǔ)言則是C風(fēng)格的,很符合人們的編程習(xí)慣。 與傳統(tǒng)的編程語(yǔ)言發(fā)展規(guī)律是相同的。
如 float temp = dot(dir,normal);
而常見(jiàn)的著色器語(yǔ)言中,低級(jí)語(yǔ)言如D3D中的LLSL,以及Adobe新出的Statge3D協(xié)同工作的AGAL.
高級(jí)語(yǔ)言如 CG,HLSL,GLSL應(yīng)該很熟了吧。
CG是NVIDIA公司出的語(yǔ)言,它可以在D3D和OPENGL中工作,但需要使用NVIDIA對(duì)應(yīng)的SDK。
HLSL是D3D協(xié)同工作的高級(jí)著色器語(yǔ)言。
GLSL是OPENGL協(xié)同工作的高級(jí)著色器語(yǔ)言。
--------------------------------------------------------------------------------------------------------------
說(shuō)了這么多,我們來(lái)看看一個(gè)簡(jiǎn)單的例子吧。 HLSL版
我打上了注釋,就不再敘述
//VERTEX SHADER
float4x4 matViewProjection; //世界-觀察-投影矩陣
struct VS_INPUT //輸入結(jié)構(gòu),這個(gè)結(jié)構(gòu)中的內(nèi)容,表示我們SHADER所關(guān)心的內(nèi)容,同時(shí),程序在進(jìn)行數(shù)據(jù)填充時(shí),應(yīng)該保證這些關(guān)注的數(shù)據(jù)被提交
{
float4 Position : POSITION0; //位置
float2 Texcoord : TEXCOORD0; //紋理信息
};
struct VS_OUTPUT //輸出結(jié)構(gòu),這個(gè)結(jié)構(gòu)中的內(nèi)容,表示此SHADER的輸出
{
float4 Position : POSITION0; //位置信息,已經(jīng)經(jīng)過(guò)坐標(biāo)系變換
float2 Texcoord : TEXCOORD0; //紋理信息
};
VS_OUTPUT vs_main( VS_INPUT Input ) //頂點(diǎn)程序的入口函數(shù)
{
VS_OUTPUT Output; //聲明一個(gè)結(jié)構(gòu)體
Output.Position = mul( Input.Position, matViewProjection ); //做矩陣變換
Output.Texcoord = Input.Texcoord; //直接輸出紋理信息, 如果你想對(duì)它做點(diǎn)手腳,是很容易的。 這就是FFP中紋理矩陣所做的事情。
return( Output );
}
//PIXEL SHADER 它就相對(duì)簡(jiǎn)單多了
sampler2D baseMap; //紋理
struct PS_INPUT //輸入結(jié)構(gòu),與VS中的輸入結(jié)構(gòu)類似,但此輸入結(jié)構(gòu)均來(lái)自于VS的輸出。
{
float2 Texcoord : TEXCOORD0; //表示我們只需要用到紋理坐標(biāo)信息
};
float4 ps_main( PS_INPUT Input ) : COLOR0 //出口函數(shù)。 COLOR0表示我們輸出的float4是用作顏色輸出, 也可以定義類似 PS_OUTPUT的結(jié)構(gòu)
{
return tex2D( baseMap, Input.Texcoord ); //很簡(jiǎn)單,就是取得對(duì)應(yīng)紋理坐標(biāo)處的像素值,輸出, 你可以在此做一些事情,比如調(diào)得更亮,或者拿另一張紋理采樣,與它混合。 混合的公式就由你自己定了,你想寫得多復(fù)雜都可以。 理論是如此。
}
//========== 感興趣的朋友可以看看上面的SHADER對(duì)應(yīng)的低級(jí)版本==========
// VS
// Generated by Microsoft (R) HLSL Shader Compiler 9.22.949.2248
//
// Parameters:
//
// float4x4 matViewProjection;
//
//
// Registers:
//
// Name Reg Size
// ----------------- ----- ----
// matViewProjection c0 4
//
vs_2_0
dcl_position v0
dcl_texcoord v1
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3
mov oT0.xy, v1
// approximately 5 instruction slots used
//PS
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.22.949.2248
//
// Parameters:
//
// sampler2D baseMap;
//
//
// Registers:
//
// Name Reg Size
// ------------ ----- ----
// baseMap s0 1
//
ps_2_0
dcl t0.xy
dcl_2d s0
texld r0, t0, s0
mov oC0, r0
// approximately 2 instruction slots used (1 texture, 1 arithmetic)
說(shuō)到這里,差不多要結(jié)束了。但還是再多說(shuō)兩句。
由于硬件條件的限制 VS和PS中對(duì)指令條數(shù)和可使用的寄存器個(gè)數(shù)都有限制,雖然隨著硬件的發(fā)展,這個(gè)限制已經(jīng)可以被忽略了。比如SM 4.0就已經(jīng)將這個(gè)限制放寬到很大。
但當(dāng)我們?cè)趯懼绦驎r(shí),除了追求效果外,還要追求效率。因此,節(jié)約使用資源將會(huì)提升效率, 一個(gè)很好我評(píng)判標(biāo)準(zhǔn)就是你的SHADER所使用的指令數(shù)
如上面低級(jí)語(yǔ)言版本中 VS,PS都有如下內(nèi)容
// approximately 5 instruction slots used
// approximately 2 instruction slots used (1 texture, 1 arithmetic)
因此,它將能夠很直觀地評(píng)估出你SHADER的效率。 但真正的結(jié)果,還是要實(shí)際測(cè)試。 由于硬件的不同,可能還存在兼容性上的問(wèn)題。
祝各位一路順風(fēng)。
上面的SHADER代碼取自 RenderDonkey 這個(gè)軟件 在ATI官網(wǎng)上可以下載。
或者,直接點(diǎn)這里http://developer.amd.com/archive/gpu/rendermonkey/pages/default.aspx
強(qiáng)勢(shì)三國(guó)卡版風(fēng)頁(yè)游《五虎上將》----等你來(lái)戰(zhàn)
《五虎上將》神跡官網(wǎng):點(diǎn)擊進(jìn)入游戲
看著五虎上線了,說(shuō)實(shí)話,心里還是有一絲竊喜,卻又一絲悲傷。
竊喜的是這也算是自己參與過(guò)的項(xiàng)目上線了,悲傷的是,這不是自己手把手從頭到尾一路走來(lái)的項(xiàng)目。
沒(méi)有那種一路風(fēng)雨走過(guò)來(lái)的感覺(jué)。
話說(shuō),雖然自己不是從五虎從頭到尾跟過(guò)來(lái)的,但我確實(shí)從五虎中學(xué)到了許多, 特別是學(xué)到了 “如果我也這樣做,那結(jié)果就是現(xiàn)在這樣”。
它就像是一面鏡子,時(shí)刻讓自己反醒。
我并不是說(shuō)五虎代碼差成什么樣,而是,這就是傳說(shuō)中的“經(jīng)驗(yàn)”。恰巧,這些東西,被我悄悄地偷取了。
由于是后入五虎項(xiàng)目組的,所以我并不能參與五虎的功能部分開發(fā)。當(dāng)然,這也是我所期望的。 我不想讓自己糾結(jié)于泥潭中無(wú)法抽身,又或者當(dāng)我抽身后,
把別人陷入了泥潭中。
因此,我只能在資源加載,瀏覽器緩存,代碼規(guī)范上給予他們幫助。
在為五虎尋找解決方案的同時(shí),另一個(gè)項(xiàng)目也在進(jìn)行著, 一個(gè)教育類養(yǎng)成頁(yè)游項(xiàng)目。于是,我將所有的方案先應(yīng)用于養(yǎng)成頁(yè)游中,經(jīng)測(cè)試和優(yōu)化完畢后,再投入五虎項(xiàng)目組。
下面是我研究和整理出來(lái)的一些方案,這些方案并不完美,甚至說(shuō)僅僅是一些很常見(jiàn)的手法。于是,我大可公布于下,不必?fù)?dān)心任何糾葛。因?yàn)檫@并非是我個(gè)人所創(chuàng)的專利,也不是五虎項(xiàng)目所特有的東西,而是我在擁有蕓蕓眾生的網(wǎng)絡(luò)中擺渡,谷歌而來(lái)。同時(shí),希望有志同道合的朋友能夠給我提出建議,相互交流,共同進(jìn)步。
資源加載相關(guān)事項(xiàng):
一、首要的任務(wù),就是要將資源請(qǐng)求打包為一個(gè)隊(duì)列。 這樣外部需要加載多個(gè)資源時(shí),就可以把隊(duì)列作為一個(gè)整體來(lái)進(jìn)行事件監(jiān)聽等。
二、其次就是資源的分類,資源至少可以分為三類, 一類是Modules.即FLEX中用來(lái)模塊化的東西,類似于C++中的DLL,(swc即類似于lib)。其次是ApplicationDomain.這多半是FLASH IDE編輯資源后的導(dǎo)出類。 最后就是純數(shù)據(jù)了, 如XML或者一些配置文件。 所以,需要將其分類,并能夠以一個(gè)很方便訪問(wèn)的方式來(lái)管理。
三、Loading方式。 Loading方式大概也可以分為兩類, 一類是阻塞式,一類是非阻塞式。 阻塞式就是那種需要出現(xiàn)Loading條的方式,使用這種方式時(shí),資源必須要完全加載完成后,才可以使用。如一些配置文件,UI組件的皮膚資源等。 非阻塞式就是不需要等待,而是在資源未加載完成時(shí),統(tǒng)一采用一個(gè)顯示資源來(lái)替換。 比如一些圖標(biāo),或者RPG地圖中一些人物的顯示(許多游戲在人物未加載完成時(shí),顯示一個(gè)球形)。
pureMVC使用注意事項(xiàng):
一、pureMVC的使用應(yīng)盡量標(biāo)準(zhǔn),如果MVC使用得不標(biāo)準(zhǔn),還不如不用。
二、盡量只在UI與業(yè)務(wù)邏輯通信時(shí)才使用pureMVC,邏輯與邏輯通信應(yīng)當(dāng)避免使用,否則會(huì)導(dǎo)致邏輯BUG的完全不可控。
模塊劃分注意事項(xiàng):
一、游戲是一個(gè)巨大的項(xiàng)目,模塊劃分在所難免。 不論是否拆分為新工程,都必須明確規(guī)定各模塊代碼所能操作的權(quán)限,若權(quán)限過(guò)大,會(huì)導(dǎo)致無(wú)力維護(hù),人走茶涼的現(xiàn)象。
二、若是采用Flex module進(jìn)行模塊劃分, 若各Modules分開建立項(xiàng)目,則需要注意文件夾的層級(jí),以及Modules編譯時(shí)相對(duì)主模塊的優(yōu)化選項(xiàng)要開啟。
三、若將所有的Module依然放到一個(gè)項(xiàng)目里,則依然要注意文件夾的層級(jí),規(guī)范各模塊代碼的操作權(quán)限,通信方式等。
UI方案:
一、Flex Button大小問(wèn)題 若采用Flex Button并使用CSS Style方案,則要注意 對(duì)于 downSkin,若需要改變大小,則要保證其up down over狀態(tài)使用的皮膚都具有同樣的大小。(特別是FLASH IDE中導(dǎo)出元件的話,更要注意,僅僅將縮小后的元件錨點(diǎn)偏移,是會(huì)失真的,因?yàn)镕LEX BUTTON只識(shí)別一個(gè)元件有效像素的包圍框作為元件尺寸。 可以新建一個(gè)透明層或者遮罩層來(lái)解決這個(gè)問(wèn)題。
二、Button的特殊性 采用CSS使用FlexButton需要將Button各幀拆開。這涉及到美術(shù)工作量問(wèn)題,所以,對(duì)于那種以圖片展現(xiàn)(即不需要在BUTTON上添加文本)的BUTTON,可以直接使用FLASH IDE導(dǎo)出SimpleButton并直接使用即可。 而對(duì)于那種需要?jiǎng)討B(tài)修改文本的按鈕,采用CSS即可。
三、CSS文件的使用。 CSS的使用可以說(shuō)極大地方便了UI的工作,其實(shí)就是將FLEX的UI換膚而已。 但是采用CSS文件方式并不適合我們。因?yàn)镃SS文件的靜態(tài)和動(dòng)態(tài)使用方式都會(huì)導(dǎo)致文件巨大。
靜態(tài)使用方式, 即<mx:Style source=”ooxx.css”/> 方式,這種方式會(huì)把 ooxx.css中所嵌入的資源都嵌入到嵌入ooxx.css樣式的SWF中來(lái)。
動(dòng)態(tài)使用方式, 即將css編譯為swf 如 ooxx.swf 并用styleManager加載。 這個(gè)方案有一個(gè)好處,就是可以動(dòng)態(tài)加載和卸載需要的CSS樣式。 看似天衣無(wú)縫的方案,卻有極大的弱點(diǎn)。 當(dāng)你查看ooxx.swf的大小時(shí),你會(huì)發(fā)現(xiàn),就算你什么資源也沒(méi)嵌入,也會(huì)有300KB+的大小。 這是因?yàn)閏ss本身是一個(gè)module.它會(huì)引入一個(gè)flex module所要import的符號(hào)。可惜,CSS編譯時(shí),并不能指定針對(duì)某個(gè)主模塊進(jìn)行優(yōu)化。 我也曾嘗試將ooxx.css嵌入到一個(gè)module A.mxml中。并在編譯時(shí)針對(duì)主模塊進(jìn)行優(yōu)化,文件減小到了60+KB. 60+KB足夠一個(gè)公共UI庫(kù)的大小了。所以,CSS文件一定要慎用。
四、CSSStyleDeclaration與setStyle. 可以通過(guò)動(dòng)態(tài)注冊(cè)CSS樣式來(lái)達(dá)到修改FLEX組件樣式的目的。 具體使用方法可以搜索這兩個(gè)關(guān)鍵字。 但是,在使用時(shí),請(qǐng)先將所需要的資源載入。
五、TabNavigator 視情況而用。
六、可拖動(dòng)窗口 并不一定要使用TitleWindow 若要使窗口可拖動(dòng),只需要將窗口startDrag()即可。 而對(duì)于許多窗口來(lái)說(shuō),我們想要實(shí)現(xiàn)的是點(diǎn)擊某個(gè)位置或者僅當(dāng)點(diǎn)擊標(biāo)題欄才拖動(dòng)。 這個(gè)時(shí)候,我們可以添加一些透明的組件在這個(gè)窗口上,監(jiān)聽這些透明的組件,并startDrag()窗口即可。
功能劃分:
一、不管是哪種游戲類型,都應(yīng)該把場(chǎng)景和UI劃分清楚,就算是鼠標(biāo)流的策略游戲,也是如此。 否則在邏輯功能上也會(huì)模糊不清。
二、游戲,永遠(yuǎn)都有場(chǎng)景和UI,沒(méi)有PAGE。。。 這是頁(yè)游和網(wǎng)頁(yè)的本質(zhì)區(qū)別。希望做網(wǎng)頁(yè)轉(zhuǎn)為做頁(yè)游的朋友能夠接受這個(gè)現(xiàn)實(shí),轉(zhuǎn)變一下思路。
----------------------------------分割線-----------------------------------
瀏覽器緩存避免
瀏覽器緩存方案也是世人皆知的,我也僅簡(jiǎn)單說(shuō)一下。 更多內(nèi)容,可以參考本BLOG中,瀏覽器緩存相關(guān)文章。
要想完成瀏覽器緩存避免需要弄清楚兩個(gè)事情 一、瀏覽器會(huì)緩存以URL全路徑方式緩存HTTP所請(qǐng)求的資源(js,html,swf,txt等等都可以)。 二、FLEX中的資源加載,是 HTTP請(qǐng)求方式。 也就是說(shuō), FLEX項(xiàng)目中加載的資源,都會(huì)被瀏覽器緩存, 這也是導(dǎo)致許多時(shí)候,游戲資源更新失敗的原因。
而要完全解決瀏覽器緩存問(wèn)題,需要保證兩個(gè)東西 第一、主文件 (比如whsj.swf)更新正常。 第二、主文件所使用的資源(如 assets/loading.swf)更新正常 .
這里,我們要用到HTTP請(qǐng)求時(shí)的傳參功能 即在請(qǐng)求的資源URL后面加上?號(hào) 如 assets/loading.swf?v=1.0 。
在時(shí)行資源請(qǐng)求時(shí),HTTP會(huì)忽略掉?號(hào),以及以后的字符。 但瀏覽器緩存會(huì)緩存整個(gè)URL路徑。 并且在進(jìn)行緩存訪問(wèn)匹配時(shí),也是采用的全路徑。 所以,我們可以在資源請(qǐng)求時(shí),給每個(gè)資源加上一個(gè)版本號(hào)。
網(wǎng)上的一個(gè)兄弟說(shuō)的采用SVN生成每一個(gè)資源的版本號(hào)方案確實(shí)很給力,但我認(rèn)為實(shí)在很麻煩。于是,我們大可在請(qǐng)求每個(gè)資源時(shí),加上游戲的發(fā)布版本號(hào)。 這樣,瀏覽器在進(jìn)行資源緩存時(shí),會(huì)將版本號(hào)信息一起緩存,當(dāng)版本更新時(shí),匹配則不會(huì)成功,從而解決了資源更新時(shí),使用了舊資源的問(wèn)題。
而最主要的,還是主文件的緩存, 因?yàn)榫退隳阗Y源更新了, 但主文件沒(méi)更新, 那么,游戲功能就不會(huì)變。 當(dāng)你采用舊版游戲功能與新版的服務(wù)器端通信時(shí),自然會(huì)出現(xiàn)各種各樣的DOWN和黑屏。 因此, 主文件也要加版本號(hào) 如whsj_1_0.swf 而每次版本發(fā)布時(shí),運(yùn)營(yíng)官網(wǎng)作相應(yīng)的更改即可。
此方案也是目前我用在五虎項(xiàng)目上的初步方案。 對(duì)于主文件版本號(hào)的管理,許多公司的做法是加一個(gè)殼, 即做一個(gè)專門用于加載主文件的swf 它幾乎不會(huì)更改, 永遠(yuǎn)都是采用隨機(jī)數(shù)或者時(shí)間戳讀取主文件版本號(hào)信息 如 version.xml?v=20111113,并使用 whsj.swf?v=1.0這樣的方式來(lái)加載主文件。 這在版本發(fā)布時(shí),可以一層不變。僅修改版本號(hào)信息文件即可。 而版本號(hào)文件采用時(shí)間戳等方式讀取,可以保證每次都加載到最新的。
最后,祝賀一下二部項(xiàng)目天地劫即將測(cè)試。
天地劫神跡官網(wǎng):點(diǎn)擊進(jìn)入
在網(wǎng)上搜瀏覽器緩存問(wèn)題時(shí),遇上了很多問(wèn)題。一是不知道應(yīng)該用何種關(guān)鍵字搜索,二是一搜出來(lái),就全是講的是如何禁用瀏覽器緩存的方案。
作為大型點(diǎn)的FLASH WEBGAME來(lái)說(shuō),不緩存顯然是不行的。總體上來(lái)說(shuō),我們要想達(dá)到的目標(biāo)就是
一、瀏覽器需要緩存
二、當(dāng)服務(wù)器資源更新時(shí),瀏覽器緩存里相應(yīng)的老版本資源失效。
下面兩篇文章講到了一個(gè)很好的解決方案,并且給出了源碼。。
更新文件避免瀏覽器緩存的解決方案(基于svn)
http://www.itamt.com/2010/06/browser_cache_prevent_base_on_svn/
更新文件避免瀏覽器緩存的解決方法(源碼)
http://www.itamt.com/2010/07/file-revision-prevent-browser-cache-svn/#comment-163
由于作者BLOG上的代碼無(wú)法下載,并且作者的實(shí)現(xiàn)與第一篇中描述的略有差異,于是我試著留言給作者。熱心的作者當(dāng)天就給了我回復(fù)。
以下是我整理后的回復(fù)內(nèi)容:
你好, 關(guān)于你說(shuō)的:
1 、你最后的方案是不是 僅僅在資源請(qǐng)求時(shí),追加一個(gè)版本號(hào)? 我發(fā)現(xiàn)IE瀏覽器緩存是將整個(gè)請(qǐng)求URL緩存下來(lái)了的。也就是說(shuō),版本號(hào)也緩存了。 這樣,當(dāng)我們使用版本號(hào)請(qǐng)求資源時(shí),就可以實(shí)現(xiàn)總是得到服務(wù)器上的資源了。
2、IE和Firefox的緩存機(jī)制差異有多大? 主要是緩存后的URL路徑上。。。
我把我的想法說(shuō)一下, 盡量表述清楚-_-:
不管IE和Firefox瀏覽器緩存都是將整個(gè)請(qǐng)求url緩存下來(lái)的. 而我的解決方案本質(zhì)與你說(shuō)的是一樣的, 即"僅僅在資源請(qǐng)求時(shí),追加一個(gè)版本號(hào)".
一般的做法,發(fā)布新版本時(shí),比如v1.5,我們大可以在所有資源請(qǐng)求都加上"....?v=1.5", 這樣做沒(méi)有錯(cuò).肯定是會(huì)避免瀏覽器緩存的.
問(wèn)題是:這種方法會(huì)重新請(qǐng)求所有的資源. 而項(xiàng)目發(fā)布的通常情況是:只有少部分資源有修改. 所以這種方法是很浪費(fèi)帶寬的, 也加重了服務(wù)器的壓力. 理想的情況就是:只重新請(qǐng)求那些被修改過(guò)的資源.
下面表述一下我的解決方案:
假設(shè)現(xiàn)在項(xiàng)目是v1.0, 我們要發(fā)布v1.5上去. 我們利用SVN產(chǎn)生一份文件, 來(lái)記錄v1.5中所有資源的SVN版本號(hào). 當(dāng)請(qǐng)求一個(gè)資源時(shí), 追加這個(gè)資源的SVN版本號(hào). 如果一個(gè)資源(比如: icon.jpg, SVN版本號(hào)是1010)在v1.5中沒(méi)有被修改, 那么它的SVN版本號(hào)與在v1.0的SVN版本號(hào)肯定是一樣的都是1010:
在v1.0中,瀏覽器請(qǐng)求icon.jpg的url是:http://.....icon.jpg?svn=1010
在v1.5中,瀏覽器請(qǐng)求icon.jpg的url是:http://.....icon.jpg?svn=1010, 這時(shí)候會(huì)直接從瀏覽器緩存中取出. 不會(huì)重新請(qǐng)求.
如果v1.5中一個(gè)資源(比如: logo.jpg, SVN版本號(hào)是1234)是修改過(guò)的, 它在v1.0的SVN版本號(hào)是1222, 請(qǐng)求資源時(shí):
在v1.0中,瀏覽器請(qǐng)求icon.jpg的url是:http://.....logo.jpg?svn=1222
在v1.5中,瀏覽器請(qǐng)求icon.jpg的url是:http://.....logo.jpg?svn=1234, 這時(shí)候與瀏覽器緩存中的請(qǐng)求是不一樣的. 所以會(huì)重新向服務(wù)端請(qǐng)求.
思路就是這樣的. 所以關(guān)鍵是利用SVN來(lái)記錄的版本號(hào)請(qǐng)求資源.
這個(gè)解決方案最終實(shí)現(xiàn)出來(lái)主要是兩個(gè)文件.
1.cur.txt記錄整個(gè)項(xiàng)目當(dāng)前的SVN版本號(hào).
2.revision.txt記錄項(xiàng)目當(dāng)前版本中所有資源的SVN版本號(hào).
整個(gè)項(xiàng)目程序一開始(Preloader.swf), Preloader.swf會(huì)先確定整個(gè)項(xiàng)目SVN版本號(hào)(加載最新cur.txt, 或者在網(wǎng)頁(yè)中指定:...Preloader.swf?cur=20), 然后根據(jù)整個(gè)項(xiàng)目SVN版本號(hào)加載revision.txt, 比如現(xiàn)在項(xiàng)目SVN版本是20, 那么Preloader.swf會(huì)加載:
http://.....revision.txt?svn=20
加載完成后, 就可以完成mFileRevisionManager的初始化...
注意Preloader.swf不是進(jìn)度條(load.swf), 它是整個(gè)項(xiàng)目最開始的地方, 所以應(yīng)該盡可能小, 而且一旦完成最不要再去修改它.
附件里,有我做的demo. 我的開發(fā)環(huán)境是FDT4.5+Flash CS5.5, 文件夾tool里一個(gè)ant腳本, 和jsfl腳本. 使用時(shí)你需要修改publish.xml開頭的路徑. 然后運(yùn)行這個(gè)ant腳本.
運(yùn)行ant前, 注意設(shè)置下:這樣ant運(yùn)行期間的輸出信息會(huì)顯示在eclipse里.
---------------------------------------------------------------
并且,我問(wèn)了一下他對(duì)Preloader.swf的理解和用法
回復(fù):
結(jié)合我參與的項(xiàng)目,說(shuō)一下Preloader.swf是干么用的:
一般項(xiàng)目肯定會(huì)有一個(gè)load.swf和main.swf, 其中
1.load.swf用于顯示加載main.swf的進(jìn)度,說(shuō)白了load.swf就是個(gè)進(jìn)度條.
2.main.swf就是整個(gè)項(xiàng)目的主程序.
而Preloader.swf是在load.swf之前加載的.也就是說(shuō)加載順序是Preloader.swf->load.swf->main.swf
Preloader.swf主要用來(lái)加載"資源版本號(hào)文件"(revision.txt, cur.txt). 并且把整個(gè)資源版本機(jī)制初始化(在我的方案里就是mFileRevisionManager).
當(dāng)然, 也完全可以把Preloader.swf并入load.swf(進(jìn)度條)中. 但我的經(jīng)驗(yàn)是load.swf(進(jìn)度條)其實(shí)是經(jīng)常需要修改更新的. 而且為了重用性考慮, 我會(huì)把Preloader.swf用于不同的項(xiàng)目中. 所以把Preloader.swf與load.swf分開.
-----------------------------分割線--------------------------------
而考慮到目前項(xiàng)目的原因,我們決定暫時(shí)采用一個(gè)很簡(jiǎn)單的方案。 就是在資源的根目錄下加版本號(hào)。
比如 1.0.0版本的資源目錄為
Assets_v1.0.0
我要加載一個(gè)ui.swf的URL就是 “Assets_v1.0.0/ui.swf”
而當(dāng)版本更新時(shí),比如到了1.0.1
我要加載一個(gè)ui.swf的URL就是 “Assets_v1.0.1/ui.swf”
這樣就像回復(fù)內(nèi)容中說(shuō)的,這會(huì)使瀏覽器緩存中所緩存的所有資源都失效。 雖然是如此,但總比用戶繼續(xù)使用老版本資源好。 并且,對(duì)于一個(gè)發(fā)布版的更新總會(huì)有時(shí)間間隔的,不會(huì)一天到晚都是這樣的情況。
而待項(xiàng)目趨于穩(wěn)定后,再集中替換成上面那種最小資源更新的解決方案。
希望上面的信息能夠幫助到和我一樣迷惑的人。如果這些信息幫助到了你,請(qǐng)去參拜本文開頭引用的兩篇文章的作者,謝謝!?。。。?!