OpenGL/OpenTK 中不一致的模型纹理

Inconsistent Model Texturing In OpenGL/OpenTK

我正在创建一个模型导入器,它将 .obj 转换为我自己的专有格式,该格式将用于我正在创建的游戏。我的模型解析器一开始似乎工作得很好,从各种游戏(如塞尔达传说)加载小模型,但无论出于何种原因,无法正确纹理化高级模型,如 Borderlands 的 Claptrap。以下是一些屏幕截图,向您展示我的意思:

使用 zelda 模型的模型加载器:

然后 Claptrap 的纹理不正确:

尽管它在 Blender 中的纹理很好:

我不明白这个,也不知道错误来自哪里。这是我的 WavefrontParser 的代码,对于我不知道的较大模型,它可能会产生一些无法预料的错误:

using System;
using System.Collections.Generic;
using System.IO;
using OpenTK;
using StardustModeling.Modeling.DataTypes;

namespace StardustModeling.Modeling.Parsers
{
    /// <summary>
    /// 
    /// Stardust Engine
    /// 
    /// A simple, lightweight WavefrontModel Parser. This
    /// class serves as the basis for the Stardust Model
    /// conversion wizard. All Stardust Models(.sdm) start
    /// out as .obj files; this class is how we load them
    /// before the conversion to .sdm begins.
    /// 
    /// Original Author: Gordon Kyle Wallace, "Krythic"
    /// 
    /// </summary>
    public static class WavefrontModelParser
    {
        /// <summary>
        /// Parses a Wavefront .obj file. The given
        /// file must be triangulated and have normals
        /// included during exportation from Blender.
        /// </summary>
        /// <param name="path">The path of the .obj on disk</param>
        /// <returns>A WavefrontModel Instance</returns>
        public static WavefrontModel Parse( string path )
        {
            WavefrontModel model = new WavefrontModel();
            VertexIndex[] verticesIndex;
            string[] wavefrontFileData = File.ReadAllLines( path );
            int loopLength = wavefrontFileData.Length; // Squeeze out every last drop!
            for( int lines = 0; lines < loopLength; lines++ )
            {
                string[] lineTokens = wavefrontFileData[ lines ].Split( ' ' );
                switch( lineTokens[ 0 ] )
                {
                    case "v": // Vector
                        float x = Single.Parse( lineTokens[ 1 ] );
                        float y = Single.Parse( lineTokens[ 2 ] );
                        float z = Single.Parse( lineTokens[ 3 ] );
                        model.Vertices.Add( new Vector3( x , y , z ) );
                        break;
                    case "vt": // Texture Coordinate
                        float u = Single.Parse( lineTokens[ 1 ] );
                        float v = Single.Parse( lineTokens[ 2 ] );
                        model.TexCoords.Add( new Vector2( u , v ) );
                        break;
                    case "vn": // Normal
                        float normalX = Single.Parse( lineTokens[ 1 ] );
                        float normalY = Single.Parse( lineTokens[ 2 ] );
                        float normalZ = Single.Parse( lineTokens[ 3 ] );
                        model.Normals.Add( new Vector3( normalX , normalY , normalZ ) );
                        break;
                    case "f":
                        verticesIndex = new VertexIndex[ 3 ];
                        for( int i = 0; i < 3; i++ )
                        {
                            string[] parameters = lineTokens[ i + 1 ].Split( '/' );
                            int vertice = Int32.Parse( parameters[ 0 ] ) - 1;
                            int texture = Int32.Parse( parameters[ 1 ] ) - 1;
                            int normal = Int32.Parse( parameters[ 2 ] ) - 1;
                            verticesIndex[ i ] = new VertexIndex( vertice , normal , texture );
                        }
                        model.Faces.Add( new Face( verticesIndex ) );
                        break;
                }
            }
            return model;
        }
    }
}

我的 WavefrontModel class:

using System.Collections.Generic;
using OpenTK;
using StardustModeling.Modeling.Parsers;

namespace StardustModeling.Modeling.DataTypes
{
    public class WavefrontModel
    {
        public List<Vector3> Vertices;
        public List<Vector2> TexCoords;
        public List<Vector3> Normals;
        public List<Face> Faces;
        public string ModelSource;

        public int TotalTriangles
        {
            get
            {
                return this.Vertices.Count/3;
            }
        }

        public WavefrontModel()
        {
            this.Vertices = new List<Vector3>();
            this.TexCoords = new List<Vector2>();
            this.Normals = new List<Vector3>();
            this.Faces = new List<Face>();
        }

        public WavefrontModel(int buffer)
        {
            this.Vertices = new List<Vector3>(buffer);
            this.TexCoords = new List<Vector2>(buffer);
            this.Normals = new List<Vector3>(buffer);
            this.Faces = new List<Face>(buffer);
        }

        public WavefrontModel(string modelPath, bool loadImmediately)
        {
            this.ModelSource = modelPath;
            if (loadImmediately)
            {
                Load();
            }
        }

        private void Load()
        {
            WavefrontModel model = WavefrontModelParser.Parse(ModelSource);
            this.Vertices = model.Vertices;
            this.TexCoords = model.TexCoords;
            this.Normals = model.Normals;
            this.Faces = model.Faces;
        }
    }

}

还有我的Materialclass,也可能会报错:

using System.Drawing;
using System.Drawing.Imaging;
using OpenTK.Graphics.OpenGL;
using PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat;

namespace StardustFramework.Framework.OpenGL.Texturing
{
    public enum MaterialType
    {
        /// <summary>
        /// Represents a Diffuse Texture
        /// </summary>
        Diffuse,
        /// <summary>
        /// Represents a Normal Texture
        /// </summary>
        Normal
    }

    public class Material
    {
        /// <summary>
        /// The name of the Material
        /// </summary>
        public string Name;
        /// <summary>
        /// The Diffuse Texture
        /// </summary>
        public int Diffuse;
        /// <summary>
        /// The Normal Texture
        /// </summary>
        public int NormalMap;
        /// <summary>
        /// The Ambient Color for the Material
        /// </summary>
        public Color AmbientColor;

        public Material( string materialName )
        {
            this.Name = materialName;
            this.AmbientColor = Color.White;
            this.Diffuse = 0;
            this.NormalMap = 0;
        }

        /// <summary>
        /// Loads a Bitmap as a Diffuse texture.
        /// </summary>
        /// <param name="bitmap">The bitmap.</param>
        public void LoadDiffuse( Bitmap bitmap)
        {
            GL.Enable( EnableCap.Texture2D );
            //GL.Hint( HintTarget.PerspectiveCorrectionHint , HintMode.Nicest );
            GL.GenTextures( 1 , out Diffuse );
            GL.BindTexture( TextureTarget.Texture2D , Diffuse );
            GL.TexParameter( TextureTarget.Texture2D , TextureParameterName.TextureMinFilter , ( int )TextureMinFilter.Nearest );
            GL.TexParameter( TextureTarget.Texture2D , TextureParameterName.TextureMagFilter , ( int )TextureMagFilter.Nearest );
            BitmapData data = bitmap.LockBits( new Rectangle( 0 , 0 , bitmap.Width , bitmap.Height ) ,
                ImageLockMode.ReadOnly , System.Drawing.Imaging.PixelFormat.Format32bppArgb );
            GL.TexImage2D( TextureTarget.Texture2D , 0 , PixelInternalFormat.Rgba , data.Width , data.Height , 0 ,
                PixelFormat.Bgra , PixelType.UnsignedByte , data.Scan0 );
            bitmap.UnlockBits( data );
            GL.BindTexture( TextureTarget.Texture2D , 0 );
        }
    }
}

如果有人能帮我发现这个错误,我会很高兴;我开始扯掉我的头发(我不必开始,哈哈)。

编辑:

我忘了说我是如何渲染的。我使用 OpenTK/OpenGL,并通过即时模式绘制(用于初始应用程序测试)。

private void DrawMesh( WavefrontModel m )
        {

            GL.Enable( EnableCap.Texture2D );
            GL.Color3( _modelMaterial.AmbientColor );
            if( _modelMaterial.Diffuse > 0 )
            {
                GL.BindTexture( TextureTarget.Texture2D , _modelMaterial.Diffuse );
            }
            GL.Begin( PrimitiveType.Triangles );
            for( int i = 0; i < m.Faces.Count; i++ )
            {
                for( int index = 0; index < m.Faces[ i ].Indices.Length; index++ )
                {
                    Vector3 v = m.Vertices[ m.Faces[ i ].Indices[ index ].Vector ];
                    Vector3 n = m.Normals[ m.Faces[ i ].Indices[ index ].Normal ];
                    Vector2 tc = m.TexCoords[ m.Faces[ i ].Indices[ index ].TextureCoordinateIndex ];
                    GL.Normal3( n.X , n.Y , n.Z );
                    GL.TexCoord2( tc.X , tc.Y );
                    GL.Vertex3( v.X , v.Y , v.Z );
                }
            }
            GL.End();
        }

经过许多痛苦的时间撕扯我的头发,我终于意识到我的问题。我没有想到的是,现在对我来说似乎很愚蠢,是我试图在 OpenGL 环境中为为 DirectX 制作的模型制作纹理。为什么这很重要?那么,在 OpenGL 中,纹理原点是左下角 0,0。在 DirectX 中,它位于左上角。所以通过简单地在 gimp 中垂直翻转纹理我让它工作了。

很高兴知道我的模型加载器没有任何问题,我只是没有考虑我使用的模型的目标 platform/api。