Shader 使用纹理图集时HLSL颜色溢出

Shader 使用纹理图集时HLSL颜色溢出,shader,textures,hlsl,atlas,Shader,Textures,Hlsl,Atlas,我正在用C#/XNA做一个游戏。我目前正在处理将用于地形的着色器。为了提高速度和效率,我使用了纹理图集,但我在瓷砖之间遇到纹理/颜色出血: 我在FXComposer和我的游戏中都得到了这种效果。这是我的着色器: //----------------------------------------------------------------------------- // InstancedModel.fx // // Microsoft XNA Community Game Platfor

我正在用C#/XNA做一个游戏。我目前正在处理将用于地形的着色器。为了提高速度和效率,我使用了纹理图集,但我在瓷砖之间遇到纹理/颜色出血:

我在FXComposer和我的游戏中都得到了这种效果。这是我的着色器:

//-----------------------------------------------------------------------------
// 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();
    }
}
//-----------------------------------------------------------------------------
//InstancedModel.fx
//
//微软XNA社区游戏平台
//版权所有(C)微软公司。版权所有。
//-----------------------------------------------------------------------------
//相机设置。
float4x4世界:世界;
float4x4视图:视图;
float4x4投影:投影;
//此采样器使用简单的Lambert照明模型。
float3 LightDirection=normalize(float3(-1,-1,-1));
float3漫射光=1.25;
float3环境光=0.25;
float-TextureSide=0//0=顶部,1=侧面,2=底部
浮动2纹理命令;
纹理;
float2-TextureSize=2.0;
采样器采样器=采样器状态
{
纹理=(纹理);
MinFilter=线性;
MipFilter=线性;
磁滤波器=线性;
地址U=夹具;
地址V=钳位;
};
结构VertexShaderInput
{
浮动4位置:位置0;
float3 Normal:NORMAL0;
float2纹理坐标:TEXCOORD0;
};
结构VertexShaderOutput
{
浮动4位置:位置0;
float4颜色:COLOR0;
float2纹理坐标:TEXCOORD0;
};
//顶点着色器辅助函数在两种技术之间共享。
VertexShaderOutput VertexShaderCommon(VertexShaderInput输入、浮点4x4实例转换、浮点2 Atlascord、浮点4颜色)
{
VertexShaderOutput输出;
//应用世界和摄影机矩阵来计算输出位置。
float4 worldPosition=mul(input.Position,instanceTransform);
float4 viewPosition=mul(世界位置,视图);
输出位置=mul(视图位置、投影);
//使用简单的Lambert模型计算照明。
float3 worldNormal=mul(input.Normal,instanceTransform);
浮动扩散量=最大值(-dot(世界法线,光照方向),0);
float3 lightingResult=饱和(扩散量*扩散光+环境光);
output.Color=float4(lightingResult,1);
output.Color=output.Color*Color;
//计算纹理坐标
float2 InputTextureCoords=input.TextureCoords;///TextureSize;
float2 InputAtlasCoords=Atlascord;///TextureSize;
float2 textCoordsActual=inputExtureCoords+inputAtlasoords;
output.texturecoordinal=textCoordsActual;
返回输出;
}
//硬件实例从次顶点流读取每个实例的世界变换。
VertexShader输出硬件安装VertexShader(VertexShaderInput输入,
float4x4实例转换:混合重量,
浮动2大西洋城1:TEXCOORD1,浮动2大西洋城2:TEXCOORD2,浮动2大西洋城3:TEXCOORD3,
浮动4颜色:彩色1)
{
float2 atlasCoord=atlasCoord1;
如果(TextureSide==1)
{
Atlascord=Atlascord1;
}
如果(TextureSide==2)
{
Atlascord=Atlascord2;
}
else if(TextureSide==3)
{
Atlascord=Atlascord3;
}
返回VertexShaderCommon(输入,mul(世界,转置(instanceTransform)),Atlascord,颜色);
}
//禁用实例化时,我们从效果参数获取世界变换。
VertexShaderOutput无安装VertexShader(VertexShaderInput,
float4x4实例转换:混合重量,
浮动2大西洋城1:TEXCOORD1,浮动2大西洋城2:TEXCOORD2,浮动2大西洋城3:TEXCOORD3,
浮动4颜色:彩色1)
{
返回VertexShaderCommon(输入、世界、纹理命令、浮点4(1,1,1,1));
}
浮动2半像素校正坐标(浮动2坐标)
{
float u=(coords.x)/TextureSize;
float v=(coords.y)/TextureSize;
返回浮点数2(u,v);
}
//这两种技术共享相同的像素着色器。
float4像素着色器函数(VertexShaderOutput输入,
浮动2 Atlascord1:TEXCOORD1:COLOR00
{                           
float2 outputExtureCoords=HalfPixileCorrectedCoords(input.textureCoords);
返回tex2D(采样器,输出exturecoords)*输入.Color;
}
//硬件实例技术。
技术硬件安装
{
通行证1
{
VertexShader=编译vs_3_0硬件安装VertexShader();
PixelShader=编译ps_3_0 PixelShaderFunction();
}
}
//用于渲染而不实例化。
安装技术
{
通行证1
{
VertexShader=编译vs_3_0 NoInstanceVertexShader();
PixelShader=编译ps_3_0 PixelShaderFunction();
}
}
我的FX Composer HLSL配置文件:

以及使用以下工具的测试atlas im: (因为我需要更多的声誉,所以不能发布,也许我可以在后续发布?)

我对此做了很多阅读,似乎我要么需要进行“半像素校正”,要么在图集中指定纹理的边缘包裹像素。这两种我都试过了,但都没有成功

问题:
如何解决我遇到的像素出血问题?

如果您想使用atlas获得漂亮的无缝平铺纹理,您必须创建一个比预期大4倍的纹理(即(2 x宽度)x(2 x高度))

更具体地说,地图集中的每个瓷砖应如下所示:

整个瓷砖应重复两次,从其中心(u,v)开始

(u,v)是atlas纹理中瓷砖的坐标

但是,在纹理生成时,此平铺应使用的坐标
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