3

我在 XNA 中使用带有自定义着色器的标准 .fbx 导入器。当我使用 BasicEffect 时,.fbx 模型已正确包裹 UV 并且具有适当的纹理。但是,当我使用自定义效果时,我必须将纹理作为参数加载,并且它没有正确映射。

问题: 1) 如何使用包含的纹理坐标和自定义效果正确地纹理我的 .fbx 模型?2) 有没有办法从加载的 .fbx 模型对象中访问纹理?这个纹理去哪里了?

注意:我研究过自定义内容管道,不相信编写自己的 Fbx 导入器/处理器会很有效。但是,如果有人可以描述性地向我提供作为答案的第一手经验,那么我将使用自定义管道。

感谢您抽出宝贵时间阅读这篇文章。

4

2 回答 2

6

这是一个老问题,但我昨天不得不自己解决这个问题,所以我想我会发布一个跟进:

如果您使用默认的 FBX 内容处理器并将DefaultEffect属性设置为BasicEffect,则可以通过以下方式获取Texture2D对象的 :

texture = ((BasicEffect)model.Meshes[0].Effects[0]).Texture;

请注意,模型中的每个网格可能具有不同的纹理。

纹理坐标与位置等一起存储在MeshPart'sVertexBuffer中。我见过两个顶点声明。对于使用单个纹理(3DS Max 中的位图材质)的模型/网格,顶点声明为VertexPositionNormalTexture.

对于具有两个纹理(位图和不透明度/alpha 映射)的模型,声明具有以下元素:

Position
Normal
Texture (usage index 0)
Texture (usage index 1)

或者,包裹在一个IVertexType结构中,

public struct VertexPositionNormalTextureTexture : IVertexType
{
    public Vector3 Position;
    public Vector3 Normal;
    public Vector4 Texture0;
    public Vector4 Texture1;

    public static VertexDeclaration VertexDeclaration
    {
        get
        {
            return new VertexDeclaration
            (
            new VertexElement(0,VertexElementFormat.Vector3, VertexElementUsage.Position, 0)
            ,
            new VertexElement(0,VertexElementFormat.Vector3, VertexElementUsage.Normal, 0)
            ,
            new VertexElement(0,VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 0)
            ,
            new VertexElement(0,VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 1)
            );
        }
    }


    VertexDeclaration IVertexType.VertexDeclaration
    {
        get { return VertexDeclaration; }
    }
}

和等效的 HLSL 结构:

struct VertexPositionNormalTextureTexture
{
    float3 Position : POSITION0;
    float3 Normal : NORMAL0;
    float4 Texture0 : TEXCOORD0;
    float4 Texture1 : TEXCOORD1;
};

请注意,我在发布此内容之前进行了更改和.Position更改,.Normal并且Vector4尚未对其进行测试。可能需要将它们改回和。Vector3float4float3Vector4float4

当然,您需要一个采样器和像素着色器中的一些基本逻辑来读取每个纹理。Texture2D假设您已经为包含颜色纹理和不透明度贴图的对象设置了两个效果参数 xTexture0 和 xTexture1 ,

// texture sampler
sampler Sampler0 = sampler_state
{
    Texture = (xTexture0);
};

sampler Sampler1 = sampler_state
{
    Texture = (xTexture1);
};

这是一个简单的两纹理像素着色器。如果您只想要一个纹理,只需从第一个采样器中读取并返回值,或应用照明等。

float4 TwoTexturePixelShader(VertexPositionNormalTextureTexture input) : COLOR0
{
    float4 texel0;
    float4 texel1;
    float4 pixel;

    // check global effect parameter to see if we want textures turned on
    // (useful for debugging geometry weirdness)
    if (TexturesEnabled)
    {
        texel0 = tex2D(Sampler0, input.Texture0);
        texel1 = tex2D(Sampler1, input.Texture1);       
        /// Assume texel1 is an alpha map, so just multiple texel0 by that alpha.
        pixel.rgb=texel0.rgb;
        pixel.a=texel0.a;
    }
    else
        /// return 100% green
        pixel = float4(0,1,0,1);

    return pixel;

} 

这里的相关点是纹理坐标已经存在于 FBX 中并且已经存储在 each MeshPart'sVertexBuffer中,因此您需要做的就是提取纹理,将其作为全局效果参数传递到着色器中,然后正常进行.

于 2011-03-24T16:36:27.333 回答
1

它不起作用的原因是您必须手动设置效果的参数,而不是依赖基本效果(这将在内容管道中设置着色器参数)。现在,我不熟悉您的着色器,所以我无法指定代码来解决您的问题......

要回答您的第二个问题,您可以通过迂回的方式绕过它。因为模型默认加载到内容管道中时具有基本效果,所以纹理被导入并分配给管道的着色器参数。因此,如果您想访问它,您必须查看模型网格部分的默认效果属性。是描述此过程的论坛帖子。

更正确的答案是完整的自定义导入器和仅使用默认值之间的折衷。您可以制作一个继承自现有模型处理器的自定义模型处理器。在那里,您可以导入您自己的自定义效果,以及您的自定义纹理和您需要设置的任何参数。并将其设置在模型内容上。有一篇文章(在 Shawn Hargreave 的博客或 msdn 上)展示了如何执行此操作,但不幸的是,我目前找不到它。

祝你好运!

于 2009-12-04T14:10:28.233 回答