地形纹理Splatting技术(翻译)
文章來源:http://www.gamedev.net/reference/articles/article2238.asp
?
Texture Splatting in Direct3D Introduction
?If you've been looking into terrain texturing techniques, you've probably heard about texture splatting. The term was coined by Charles Bloom, who discussed it at http://www.cbloom.com/3d/techdocs/splatting.txt. With no disrespect to Charles Bloom, it was not the most clear or concise of articles, and has left many confused in its wake. While many use and recommend it, few have taken the time to adequately explain it. I hope to clear up the mystery surrounding it and demonstrate how to implement it in your own terrain engine.
????? 如果有已經學習過地形紋理技術,你很可能已經聽說過紋理splatting技術。這個術語是Charles Bloom創造的,他在http://www.cbloom.com/3d/techdocs/splatting.txt里對這個技術進行了闡述。并不是想對Charles Bloom失禮,他所寫的并不是一篇條理清晰的文章,存在許多的令人迷惑的地方。使用它的大部分人少有對其進行充分的解釋。我希望自己能揭開圍繞它的迷霧,并闡述如何在你自己的地形引擎中實現這個技術。
?
?The Basics
?What is texture splatting? In its simplest form, it is a way to blend textures together on a surface using alphamaps.
那什么是splatting呢?? 最簡單的解釋如下:它是一種使用alphamaps技術將紋理融合到表面的技術。
?
?I will use the term alphamap to refer to a grayscale image residing in a single channel of a texture. It can be any channel alpha, red, green, blue, or even luminance. In texture splatting, it will be used to control how much of a texture appears at a given location. This is done by a simple multiplication, alphamap * texture. If the value of a pexel in the alphamap is 1, that texture will appear there in full value; if the value of a pexel in the alphamap is 0, that texture will not appear there at all.
我將使用術語alphamap來關聯屬于紋理中某通道的顏色。一個紋理中通常有多個通道:紅、綠、藍、或者是亮度。在紋理的splatting中,alphamap將用于控制該紋理在當前位置顯示顏色的多少。通過一個簡單的乘法很容易做到:alphamap * texture(texture指代當前位置紋理的顏色值)。如果某像素的alphamap是1,則紋理顯示全值,如果某像素的alphamap是0,則該紋理完全不顯示。
?
?For terrain, the texture might be grass, dirt, rock, snow, or any other type of terrain you can think of. Bloom refers to a texture and its corresponding alphamap as a splat. An analogy would be throwing a glob of paint at a canvas. The splat is wherever you see that glob. Multiple globs of paint layered on top of each other create the final picture.
在地形里,紋理可能是草、泥土、巖石、雪或者你能想到的其他類型。Bloom將紋理和其對應的alphamap值統稱為一個splat。類似于在帆布上涂鴉的效果。splat就是無處不在的小水滴,通過在繪制層混合這些水滴創建最好的圖像。
?
?Let's say you have a 128x128 heightmap terrain, divided into 32x32 chunks. Each chunk would then be made up of 33x33 vertices. Each chunk has the base textures repeated several times over it ?but the alphamap is stretched over the entire area. (0, 0) of the chunk would have alphamap coordinates of (0, 0) and texture coordinates of (0, 0). (33, 33) of the chunk would have alphamap coordinates of (1, 1) and texture coordinates of (x, x), where x is the number of times you want the textures to be repeated. x will depend on the resolution of the textures. Try to make sure they repeat enough to make detail up close, but not so much that the repetition is obvious from far away.
假設你有一個128×128的地形高度圖,將其分割為32×32的塊。每塊由33×33個頂點構成。每塊通過平鋪貼圖的形式貼上了基本的紋理。但alphamap貼圖覆蓋了整個塊的區域,塊(0, 0)的位置對應alphamap坐標和紋理坐標(0, 0)的位置,第(33, 33)個頂點alphamap坐標為(1,1),而紋理坐標為 (x, x),x是你想讓紋理重復的次數。x視紋理的分辨率而定。應該通過平鋪紋理確保紋理有足夠的細節,但從遠處看的話,細節就不重要了。
?
?The resolution of your alphamap per chunk is something you need to decide for yourself, but I recommend powers of two. For a 32×32 chunk, you could have a 32×32 alphamap (one texel per unit), a 64×64 alphamap (two texels per unit), or even a 128×128 alphamap (four texels per unit). When deciding what resolution to use, remember that you need an alphamap for every texture that appears on a given chunk. The higher the resolution is, the more control over the blending you have, at the cost of memory.
每個塊的alphamap的分辨率需要你去制定,但我建議是2的次方。如果是32×32的塊,你可以創建一個32×32 alphamap(每個單位一個texel),64×64 alphamap或者是128×128 alphamap。在選擇使用哪個分辨率的時候,記住你需要為每個呈現于所給塊上的紋理一個alphamap(譯者:如果在一個塊上你打算使用4種紋理的元素,那么這個塊將由4個alphamap進行混合而得到)。分辨率越高,你需要做的混合就越多,內存消耗也越大。
?The size of the chunk is a little trickier to decide. Too small and you will have too many state changes and draw calls, too large and the alphamaps may contain mostly empty space. For example, if you decided to create an alphamap with 1 texel per unit with a chunk size of 128x128, but the alphamap only has non-zero values in one small 4x4 region, 124x124 of your alphamap is wasted memory. If your chunk size was 32x32, only 28x28 would be wasted. This brings up a point: if a given texture does not appear at all over a given chunk, don't give that chunk an alphamap for that texture.
塊大小的選擇有一定的技巧。太小的話,你將涉及過多的狀態改變和繪制調用。太到的話,alphamaps有可能包含過多的空區域。例如:如果你打算創建一個一單位一個texel的128×128的塊,但alphamap僅僅在一個4×4的小區域有非零的值,124×124的內存空間都被浪費了。如果塊是32×32,那么浪費的空間僅僅是28×28。這是一個需注意的地方:如果給的紋理并不覆蓋塊的所有區域,那么不必為塊提供
與紋理相關的alphamap。
?
?The reason the terrain is divided into chunks is now apparent. Firstly, and most importantly, it can save video memory, and lots of it. Secondly, it can reduce fillrate consumption. By using smaller textures, the video card has less sampling to do if the texture does not appear on every chunk. Thirdly, it fits into common level of detail techniques such as geomipmapping that require the terrain to be divided into chunks anyway.
為什么地形需要分為塊的原因逐漸明朗。首先,分塊能節省大量顯存空間。再次,它降低了速度上的消耗。使用較小紋理時,如果紋理不出現在塊中,顯卡的采樣處理將減小。第三,這種方法適用于geomipmapping等需要地形切分的LOD地形技術。
?
?Creating the Blends
?The key to getting smooth blending is linear interpolation of the alphamaps. Suppose there is a1 right next to a0. When the alphamap is stretched out over the terrain, Direct3D creates an even blend between the two values. The stretched alphamap is then combined with terrain texture, causing the texture itself to blend.
?Rendering then becomes the simple matter of going through each chunk and rendering the splats on it. Generally, the first splat will be completely opaque, with the following splats having varying values in their alphamaps. Let me demonstrate with some graphics. Let's say the first splat is dirt. Because it is the first that appears on that chunk, it will have an alphamap that is completely solid.
獲得平滑混合效果的關鍵是對alphamaps進行線性插值。假設a1在a0的右邊。當alphamap延展到整個地形,Direct3D在兩個值之間創建平滑的混合。延展的alphamap用紋理連接起來促使了紋理自身的混合。
?遍歷每個塊并在其上繪制splat是一個簡單的小問題。通常,第一個splat將是完全不透明的,接下來的splats的值根據alphamaps將有變換。讓我們用一些圖進行論證。第一個splat為泥土層。因為泥土更像是在塊中的最底層顯示的東西,它的alphamap應該是完全不透明的。
? *?? =?
?
? After the first splat is rendered, the chunk is covered with dirt. Then a grass layer is added on top of that:
第一個splat繪制后,塊被泥土覆蓋。結著草層將被加在上邊。
?
?The process is repeated for the rest of the splats for the chunk.
?It is important that you render everything in the same order for each chunk. Splat addition is not commutative. Skipping splats won't cause any harm, but if you change the order around, another chunk could end up looking like this:
?剩下的splats對于塊的操作都是類似的重復。為每個塊繪制東西的順序是十分重要的。Splat混合的順序不可交換。不進行splats混合倒不會有什么問題,但如果你改變了周圍塊的繪制順序,另一個塊的繪制結果如下:
?
?The grass splat is covered up because the dirt splat is completely opaque and was rendered second.
?You may be wondering why the first splat should be opaque. Let's say it wasn't, and instead was only solid where the grass splat was clear. Here's what happens:
因為泥土splat完全不透明,并且是第二次繪制的,致使草層被掩蓋。
你可能對第一個splat要保持不透明有所疑問。讓我們將其設為透明試試,替代圖的一部分草層splat被清空。結果如下:
?
?It's obvious this does not look right when compared with the blend from before. By having the first splat be completely opaque, you prevent any roles?from appearing like in the picture above.
很明顯,混合后的效果并不是很好。但如果將第一個splat設置為完全透明的話,效果就不一樣了。(這里不知如何翻譯合適)
?
?Creating the Alphamaps
?Now that we know what texture splatting is, we need to create the alphamaps to describe our canvas. But how do we decide what values to give the alphamaps?
?Some people base it off of terrain height, but I recommend giving the ability to make the alphamaps whatever you want them to be. This gives you the flexibility to put any texture wherever you want with no limitations. It's as simple as drawing out the channels in your paint program of choice. Even better, you could create a simple world editor that allows the artist to see their alphamap and interact with it in the actual world.
?
現在我們了解了什么是texture splatting,我們需要創建alphamaps來描述涂鴉用的帆布。但我們該往alphamaps上填充什么值呢?
?一些人創建alphamaps時脫離了地形高度值,但我建議讀者最好有控制alphamaps的能力。這是你將任何紋理置于你所希望的地方。在你的程序中加入繪制通道。如果你能創建一個簡單的世界編輯器允許美工看到alphamap,并能夠將它融入實際的世界中。
?
?Implementation
?Let's take a step back and look at what we have: Some sort of terrain representation, such as a heightmap A set of textures to be rendered on the terrain. An alphamap for each texture
Look at #3. We know that each alphamap has to be in a texture. Does this mean that every alphamap needs its own texture? Thankfully, the answer is no. Because the alphamap only has to be in a single channel of a texture, we can pack up to four alphamaps into a single texture ?one in red, one in green, one in blue, and one in alpha. In order to access these individual channels we will need to use a pixel shader, and because we need five textures (one with the alphamaps and four to blend), PS 1.4 is required. Unfortunately this is a still stiff requirement, so I will show how to use texture splatting with the fixed function pipeline as well as with a pixel shader.
實現
?退一步說,看看我們將實現什么:某種地面的表示,例如貼了多種紋理的高度圖A需要繪制到地形。每個紋理的alphamap如 #3。我們知道每個alphamap都呈現一個紋理。這是否意味著每個alphamap需要其自身的紋理呢?謝天謝地,答案是否定的。一個alphamap只需占用紋理的一個通道。我們可以將四個alphamap打包進一張紋理中,r、g、b、a四個通道。我們需要使用 5張紋理,一張用于alphamap,四張用于混合。PS shader1.4的支持是必須的。不幸的是,硬件的要求比較高,所以我將介紹一種使用固定管線的繪制方法實現紋理的splatting。
?Splatting with the Fixed Function Pipeline
?Using the fixed function pipeline has one benefit that the pixel shader technique lacks: it will run on virtually any video card. All it requires is one texture unit for the alphamap, one texture unit for the texture, and the correct blending states.
?I chose to put the alphamap in stage 0 and the texture in stage 1. This was to stay consistent with the pixel shader, which makes most sense with the alphamap in stage 0. The texture stage states are relatively straightforward from there. Stage 0 passes its alpha value up to stage 1. Stage 1 uses that alpha value as its own and pairs it with its own color value.
當顯卡不支持著色技術時,固定管線的繪制方法便顯示其益處:這種方法可運行在基本所有的顯卡上。所需的僅僅是一個用于alphamap的紋理單位,一個用于紋理的紋理單位以及正確的混合狀態設置。
?
// Alphamap: take the alpha from the alphamap, we don't care about the color
g_Direct3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
g_Direct3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
// Texture: take the color from the texture, take the alpha from the previous stage
g_Direct3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
g_Direct3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_Direct3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
g_Direct3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
?We have to set the blending render states as well in order to get the multiple splats to combine together correctly. D3DRS_SRCBLEND is the alpha coming from the splat being rendered, so we set it to D3DBLEND_SRCALPHA. The final equation we want is FinalColor = Alpha * Texture + (1 ?Alpha) * PreviousColor. This is done by setting D3DRS_DESTBLEND to D3DBLEND_INVSRCALPHA.
g_Direct3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
g_Direct3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_Direct3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
我們必須正確地設置繪制狀態使得splats被正確地連接起來。D3DRS_SRCBLEND是splat被繪制所需的alpha狀態,所以我們設置D3DBLEND_SRCALPHA?;旌瞎饺缦?#xff1a;FinalColor = Alpha * Texture + (1-Alpha) * PreviousColor。通過設置D3DRS_DESTBLEND to D3DBLEND_INVSRCALPHA,可以達到這種混合效果。
?
?Splatting with a Pixel Shader
Why even bother with the pixel shader? Using all the channels available in a texture instead of only one saves memory. It also allows us to render four splats in a single pass, reducing the number of vertices that need to be transformed. Because all of the texture combining takes place in the shader, there are no texture stage states to worry about. We just load the texture with an alphamap in each channel into stage 0, the textures into stages 1 through 4, and render.
為什么恰好要涉及pixel shader呢?使用紋理中的多個通道來替換傳統的僅僅是節省內存的技術。它容許我們在一個繪制過程里渲染四個splats,降低了需要變換的頂點數。因為所有的紋理混合發生在shader操作中,所以無需關心紋理的stage狀態。我們正好將每個通道的alphamap放在stage 0,紋理放在stage 1~4,然后進行繪制。
ps_1_4
// r0: alphamaps
// r1 - r4: textures
// Sample textures
texld r0, t0??//t0 放在 r0存儲器中
texld r1, t1??//
texld r2, t1
texld r3, t1
texld r4, t1
// Combine the textures together based off of their alphamaps
mul r1, r1, r0.x??//r1 = r0.x * r1
lrp r2, r0.y, r2, r1?//以r0.y為參數在r2與r1之間做插值,結果放在r2中
lrp r3, r0.z, r3, r2?//以r0.z為參數在r3與r2之間做插值,結果放在r3中
lrp r0, r0.w, r4, r3?//以r0.w為參數在r4與r3之間做插值,結果放在r0中
?The mul instruction multiplies the first texture by its alphamap, which is stored in the red channel of the texture in sampler 0. The lrp instruction does the following arithmetic: dest = src0 * src1 + (1 - src0) * src2. Let's say r0.x is the alphamap for a dirt texture stored in r1, and r0.y is the alphamap for a grass texture stored in r2. r2 contains the following after the first lrp: GrassAlpha * GrassTexture + (1-GrassAlpha) * DirtBlended, where DirtBlended is DirtAlpha * DirtTexture. As you can see, lrp does the same thing as the render states and texture stage states we set before. The final lrp uses r0 as the destination register, which is the register used as the final pixel color. This eliminates the need for a final mov instruction.
乘法指令將sampler0紋理的x值與sampler1紋理的值相乘. lrp指令做了一下的計算:dest = src0 * src1 + (1 - src0) * src2.
我們可以這樣理解:r0.x是存于r1的灰土紋理的alphamap分量,r0.y是存于r2的草紋理的alphamap分量,依此類推。經過第一個lrp操作后r2的值將變為:GrassAlpha * GrassTexture + (1-GrassAlpha) * DirtBlended。正如你所看到的,lrp所做的與之前提到的固定管線繪制操作一樣。最后的lrp操作將r0作為目標寄存器,該寄存器的值便是最后的像素顏色。這省去(eliminate)了一個mov指令。
?
?What if you only need to render two or three splats for a chunk? If you want to reuse the pixel shader, simply have the remaining channels be filled with 0. That way they will have no influence on the final result. You could also create another pixel shader for two splats and a third for three splats, but the additional overhead of more SetPixelShader calls may be less efficient than using an extra instruction or two.
如果你僅僅要為一個chunk繪制2到3個splats,那什么是你需要的呢?如果你想重復使用 pixel shader ,要實現的功能僅僅是將剩余沒有到的通道置0。這種方式將不會對最后的結果產生影響。你也可以針對2個splat的操作創建另一個pixel shader,也可以創建第三個pixel shader用于3個splat的操作,但是SetPixelShader帶來的額外的開銷比使用一個額外的指令效率低。
?
?Multiple passes are required if you need to render more than four splats for a chunk. Let's say you have to render seven splats. You first render the first four, leaving three left. The alpha channel of your second alphamap texture would be filled with 0, causing the fourth texture to cancel out in the equation. You simply set the alphamap texture and the three textures to be blended and render. The D3DRS_BLEND and D3DRS_SRCBLEND stages from before perform the same thing as the lrp in the pixel shader, allowing the second pass to combine seamlessly with the first.
如果你想繪制多于4個splats的時候,就需要多passes的操作。假設需要渲染7個splat。你先繪制前4個。第二個alphamap未用到的alpha通道將被填充為0, 使第四個紋理從方程中剔除。你僅僅需要設置alphamap和剩余的三個紋理進行混合和渲染。前面介紹的D3DRS_BLEND和D3DRS_SRCBLEND渲染狀態設置可實現同樣的效果,將第二個pass與前面的pass混合。
?The Demo
?The demo application uses the two techniques described here to render a texture splatted quad. I decided not to go for a full heightmap to make it as easy as possible to find the key parts in texture splatting. Because of this, the demo is completely fillrate limited. The initial overhead of the pixel shader may cause some video cards to perform worse with it than with its fixed function equivalent, so take the frame rates with a grain of salt. The pixel shader will almost always come out ahead in a more complex scene.
?You can toggle between the fixed function pipeline and the pixel shader through the option in the View menu.
?The textures used are property of nVidia? and are available in their full resolution at http://developer.nvidia.com/object/IO_TTVol_01.html.
演示程序使用了兩種技術描述了渲染splatted四邊形的方法。為了簡化splatting操作,我沒有引入一個完整的高度圖。也正因為如此,演示程序的功能有一定的限制。
?
Sources
?Terrain Texture Compositing by Blending in the Frame-Buffer by Charles Bloom, http://www.cbloom.com/3d/techdocs/splatting.txt
?And, of course, the helpful people at http://www.gamedev.net
?Feel free to send any questions or comments to nglasser@charter.net or private message Raloth on the forums!
?
----------------------------------------------------------------------------------------
譯注:文中用的是比較早的shader版本,用匯編寫的,我改成了通俗的版本:
float4 main_ps(float2 iTexCoord0: TEXCOORD0) : COLOR
{?
? float3 cov1 = tex2D(SplatMap0, iTexCoord0).rgb;
? float3 cov2 = tex2D(SplatMap1, iTexCoord0).rgb;
?
? float4 oColor;
? oColor = tex2D(TexSplat0, iTexCoord0) * cov1.x
?????????? + tex2D(TexSplat1, iTexCoord0) * cov1.y
?????????? + tex2D(TexSplat2, iTexCoord0) * cov1.z
?????????? + tex2D(TexSplat3, iTexCoord0) * cov2.x
?????????? + tex2D(TexSplat4, iTexCoord0) * cov2.y
?????????? + tex2D(TexSplat5, iTexCoord0) * cov2.z;
??
?? return oColor;
}
?
另外,如顯存及帶寬足夠大的話,用于Splatting的地形紋理可以組合在一張紋理中,減少創建紋理的開銷,類似這樣:
對應的shader代碼可以改成:
float4 main_ps(float2 iTexCoord0: TEXCOORD0) : COLOR
{
?
? float4 oColor;???
? float3 cov1 = tex2D(SplatMap0, iTexCoord0).rgb;
? float3 cov2 = tex2D(SplatMap1, iTexCoord0).rgb;
?
? float2 splatTexCoord0;
? float2 splatTexCoord1;
? float2 splatTexCoord2;
? float2 splatTexCoord3;
?
? iTexCoord0.x = fmod( iTexCoord0.x,1.0 );
? iTexCoord0.y = fmod( iTexCoord0.y,1.0 );
??
? splatTexCoord0 = iTexCoord0/2.0;
? splatTexCoord1 = splatTexCoord0 + float2( 0.5,0.0 );
? splatTexCoord2 = splatTexCoord0 + float2( 0.0,0.5 );?
? splatTexCoord3 = splatTexCoord0 + float2( 0.5,0.5 );?
?
? oColor = tex2D(TexSplat0, splatTexCoord0) * cov1.x
?????????? + tex2D(TexSplat0, splatTexCoord1) * cov1.y
?????????? + tex2D(TexSplat0, splatTexCoord2) * cov1.z
?????????? + tex2D(TexSplat0, splatTexCoord3) * cov2.x
?????????? + tex2D(TexSplat1, splatTexCoord0) * cov2.y
?????????? + tex2D(TexSplat1, splatTexCoord1) * cov2.z;
??
?? return oColor;
}
?----------------------------------------------------------------------------------------
轉載于:https://www.cnblogs.com/dawn/archive/2010/05/09/1730952.html
總結
以上是生活随笔為你收集整理的地形纹理Splatting技术(翻译)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 针对$_SERVER[’PHP_SELF
- 下一篇: 使用RegularExpressionV