2013-04-17 65 views
3

我在C#/ XNA中製作遊戲。我目前正在研究我將用於地形的着色器。我使用的紋理地圖集的速度和效率,但我遇到瓷磚之間紋理/顏色出血: http://i.imgur.com/lZcESsn.png使用紋理地圖集時的HLSL顏色流血

我在FX作曲家和我的遊戲本身得到這種效果。這裏是我的着色器:

//----------------------------------------------------------------------------- 
// InstancedModel.fx 
// 
// Microsoft XNA Community Game Platform 
// Copyright (C) Microsoft Corporation. All rights reserved. 
//----------------------------------------------------------------------------- 


// Camera settings. 
float4x4 World : World < string UIWidget="None"; >; 
float4x4 View : View < string UIWidget="None"; >; 
float4x4 Projection : Projection < string UIWidget="None"; >; 

// This sampler uses a simple Lambert lighting model. 
float3 LightDirection = normalize(float3(-1, -1, -1)); 
float3 DiffuseLight = 1.25; 
float3 AmbientLight = 0.25; 

float TextureSide = 0; //0 = top, 1 = side, 2 = bottom 
float2 TextureCoord; 
texture Texture; 
float2 TextureSize = 2.0; 

sampler Sampler = sampler_state 
{ 
    Texture = (Texture); 
    MinFilter = Linear; 
    MipFilter = Linear; 
    MagFilter = Linear; 
    AddressU = Clamp; 
    AddressV = Clamp; 
}; 


struct VertexShaderInput 
{ 
    float4 Position : POSITION0; 
    float3 Normal : NORMAL0; 
    float2 TextureCoordinate : TEXCOORD0; 
}; 


struct VertexShaderOutput 
{ 
    float4 Position : POSITION0; 
    float4 Color : COLOR0; 
    float2 TextureCoordinate : TEXCOORD0; 
}; 

// Vertex shader helper function shared between the two techniques. 
VertexShaderOutput VertexShaderCommon(VertexShaderInput input, float4x4 instanceTransform, float2 atlasCoord, float4 colour) 
{ 
    VertexShaderOutput output; 

    // Apply the world and camera matrices to compute the output position. 
    float4 worldPosition = mul(input.Position, instanceTransform); 
    float4 viewPosition = mul(worldPosition, View); 
    output.Position = mul(viewPosition, Projection); 

    // Compute lighting, using a simple Lambert model. 
    float3 worldNormal = mul(input.Normal, instanceTransform);  
    float diffuseAmount = max(-dot(worldNormal, LightDirection), 0);  
    float3 lightingResult = saturate(diffuseAmount * DiffuseLight + AmbientLight);  
    output.Color = float4(lightingResult, 1); 
    output.Color = output.Color * colour; 

    //calculate texture coords 
    float2 InputTextureCoords = input.TextureCoordinate;///TextureSize; 
    float2 InputAtlasCoords = atlasCoord;///TextureSize; 

    float2 textCoordsActual = InputTextureCoords + InputAtlasCoords; 

    output.TextureCoordinate = textCoordsActual; 

    return output; 
} 


// Hardware instancing reads the per-instance world transform from a secondary vertex stream. 
VertexShaderOutput HardwareInstancingVertexShader(VertexShaderInput input, 
                float4x4 instanceTransform : BLENDWEIGHT, 
                float2 atlasCoord1 : TEXCOORD1, float2 atlasCoord2 : TEXCOORD2, float2 atlasCoord3 : TEXCOORD3, 
                float4 colour : COLOR1) 
{ 
    float2 atlasCoord = atlasCoord1; 
    if (TextureSide == 1) 
    { 
     atlasCoord = atlasCoord1; 
    } 
    if (TextureSide == 2) 
    { 
     atlasCoord = atlasCoord2; 
    } 
    else if (TextureSide == 3) 
    { 
     atlasCoord = atlasCoord3; 
    } 
    return VertexShaderCommon(input, mul(World, transpose(instanceTransform)), atlasCoord, colour); 
} 


// When instancing is disabled we take the world transform from an effect parameter. 
VertexShaderOutput NoInstancingVertexShader(VertexShaderInput input, 
                float4x4 instanceTransform : BLENDWEIGHT, 
                float2 atlasCoord1 : TEXCOORD1, float2 atlasCoord2 : TEXCOORD2, float2 atlasCoord3 : TEXCOORD3, 
                float4 colour : COLOR1) 
{ 
    return VertexShaderCommon(input, World, TextureCoord, float4(1,1,1,1)); 
} 

float2 HalfPixileCorrectedCoords(float2 coords) 
{ 
    float u = (coords.x)/TextureSize; 
    float v = (coords.y)/TextureSize; 

    return float2(u, v); 
} 

// Both techniques share this same pixel shader. 
float4 PixelShaderFunction(VertexShaderOutput input, 
          float2 atlasCoord1 : TEXCOORD1) : COLOR00 
{       
    float2 outputTextureCoords = HalfPixileCorrectedCoords(input.TextureCoordinate);  
    return tex2D(Sampler, outputTextureCoords) * input.Color; 
} 


// Hardware instancing technique. 
technique HardwareInstancing 
{ 
    pass Pass1 
    { 
     VertexShader = compile vs_3_0 HardwareInstancingVertexShader(); 
     PixelShader = compile ps_3_0 PixelShaderFunction(); 
    } 
} 

// For rendering without instancing. 
technique NoInstancing 
{ 
    pass Pass1 
    { 
     VertexShader = compile vs_3_0 NoInstancingVertexShader(); 
     PixelShader = compile ps_3_0 PixelShaderFunction(); 
    } 
} 

我的FX Composer的HLSL簡介: http://i.imgur.com/wNzmPXA.png

,並使用測試圖譜IM: (廣東話職位,因爲我需要更多的聲譽,我也許可以將它張貼在隨訪? )

我已經做了很多關於這方面的閱讀,似乎我需要做一個「半像素校正」或者將像素包裹在地圖集中指定紋理的邊緣。我已經嘗試了這兩個都沒有成功。

問題: 如何解決我遇到的像素出血問題?

+0

紋理圖集:http://i.imgur.com/MYUbGKm.jpg – user2002287

回答

5

如果你想使用地圖集來獲得漂亮的無縫拼接紋理,你必須創建一個比你期望的大4倍的紋理(即(2 x寬)x(2 x高)) 。

更具體地說,在阿特拉斯每個瓦片應該是這樣的: image

整個瓷磚應重複兩次,在其中心開始(U,V)。

(u,v)是地圖集紋理中瓦片的座標。

但是,你應該同時紋理對象使用這種瓷磚的座標爲:

(U0,V0)< --->(U1,V1)

可以計算它們如下:

rw = tile_width/atlas_width 
rh = tile_height/atlas_height 
u0 = u + 0.5 * rw 
v0 = v + 0.5 * rh 
u1 = u0 + rw 
v1 = v0 + rh 

使用紋理圖集時出現顏色出血的主要問題之一是mipmapping。當創建mipmap時,紋理將被下采樣,並且相鄰的拼貼將被混合在一起,從而導致僞影。我上面描述的方法通過提供足夠的紋理區域保留來防止它。

當紋理採樣紋理時出現僞影的另一個原因是紋理過濾。上述方法也對此有所幫助,因爲在範圍(u0,v0) - (u1,v1)中的樣本附近總是存在由瓦片紋理覆蓋的足夠區域。

+0

非常感謝您的回覆! 你建議應該工作,但效率非常低。我可能不得不依靠這些方面的東西,但我真的希望有一個更有效的解決方案。 這引入的一個問題是我可以使用的紋理數量的限制。我打算使用2048x2048的最大阿特拉斯尺寸,紋理爲128 * 128(總共256個紋理)。這將限制我128個紋理。這可能是我必須努力工作的內容,但我真的希望避免。 – user2002287

+0

我一直在做更多的閱讀,它看起來像你的解決方案是最強大的..我想我將不得不限制我的紋理呢!或者,如果我達到了128個限制,可能會在着色器中添加兩個紋理... – user2002287

+0

實際上,對於2048x2048圖集和128x128紋理,限制爲64.這是因爲您需要每個紋理的256x256子集, 256)*(2048/256)= 8 * 8 = 64可用紋理。 – miloszmaki