【问题标题】:Inconsistent Model Texturing In OpenGL/OpenTKOpenGL/OpenTK 中的模型纹理不一致
【发布时间】:2016-02-01 21:03:02
【问题描述】:

我正在创建一个模型导入器,它将 .obj 转换为我自己的专有格式,该格式将用于我正在创建的游戏。我的模型解析器一开始似乎工作得很好,可以从各种游戏中加载小型模型,例如《塞尔达传说》,但由于某种原因,无法正确地为高级模型(例如《无主之地》中的 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 类:

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;
        }
    }

}

还有我的 Material 类,这也可能产生错误:

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();
        }

【问题讨论】:

  • 我刚躺下睡觉,当一个可能的解决方案袭击我时,我开始打瞌睡。我认为问题在于对浮点数的解析。在将字符串转换为浮点数时(当浮点数更抽象时,例如使用高级 uv 建模时),我可能会出现一些退化。明天我会做一些调试。不过,它可能是完全不相关的其他东西。
  • 反对票的理由?如有必要,我可以提供更多代码。仅仅因为一个问题很复杂并不值得反对。

标签: c# opengl modeling opentk uv-mapping


【解决方案1】:

在撕扯我的头发很多小时后,我终于意识到了我的问题。我没有想到,现在对我来说似乎很愚蠢的是,我试图在 OpenGL 环境中对为 DirectX 制作的模型进行纹理处理。为什么这很重要?好吧,在 OpenGL 中,纹理原点是左下角 0,0。在 DirectX 中,它位于左上角。因此,只需在 gimp 中垂直翻转纹理,我就可以使用它。

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

【讨论】:

  • 最终结果是一样的,但我认为更多的是在OBJ文件中通常如何指定纹理坐标的问题。至少对于我看到的文件,您需要翻转纹理坐标的 y 分量以用于 OpenGL。无需更改文件或纹理,只需在读取文件时翻转纹理坐标即可。
  • @RetoKoradi 如果你有时间,你认为你可以看看这个新的相关问题吗?它涉及 Blender 如何明显检测何时翻转纹理坐标(如您所提到的)以及何时忽略这样做。两个 .obj 模型(无论是 OpenGL 还是 DirectX 目标平台)都在 Blender 中正确渲染。我很好奇它是如何做到这一点的。也许你知道答案。干杯。 =) blender.stackexchange.com/questions/40958/…
猜你喜欢
  • 2020-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-13
  • 2012-02-07
  • 1970-01-01
  • 2019-10-07
  • 2017-05-28
相关资源
最近更新 更多