加载模型时奇怪的OpenGL纹理坐标

时间:2014-09-14 18:45:41

标签: c# opengl blender opentk

我决定在我的OpenTK C#游戏中实现一个模型加载器;除了纹理坐标完全奇怪之外,它呈现得很好。我使用的代码是我在互联网上找到的OpenTK模型加载器的略微修改版本。我已经尝试过在搅拌机中解开我的模型,但似乎结果总是一样紧张。

以下是我的模型在Blender中的样子:

enter image description here

这就是我游戏中的样子:

enter image description here

这是我的WavefrontModel类:

using System.Collections.Generic;
using OpenTK;

namespace GameProject.Game.Framework.ModelLoader {
    public class WavefrontModel {
        public List<Vector3> Vertices;
        public List<Vector2> TexCoords;
        public List<Vector3> Normals;
        public List<Face> Faces;

        public WavefrontModel( string modelPath ) {
            loadModel( modelPath );
        }

        public WavefrontModel(
            List<Vector3> points , List<Vector2> texCoords , List<Vector3> normals , List<Face> faces ) {
            this.Vertices = points;
            this.TexCoords = texCoords;
            this.Normals = normals;
            this.Faces = faces;
        }

        private void loadModel( string modelPath ) {
            WavefrontModel model = new WavefrontModelLoader().LoadFile( modelPath );
            this.Vertices = model.Vertices;
            this.TexCoords = model.TexCoords;
            this.Normals = model.Normals;
            this.Faces = model.Faces;
        }
    }

    public struct ModelVertex {
        public int Vertex;
        public int Normal;
        public int TexCoord;

        public ModelVertex( int v , int n , int t ) {
            Vertex = v;
            Normal = n;
            TexCoord = t;
        }
    }

    public struct Face {
        public ModelVertex[] Points;

        public Face( int i ) {
            Points = new ModelVertex[ i ];
        }

        public Face( ModelVertex[] i ) {
            Points = i;
        }
    }
}

这是我的WavefrontModelLoader类:

using System.Collections.Generic;
using System.IO;
using OpenTK;

namespace GameProject.Game.Framework.ModelLoader {
    public class WavefrontModelLoader {

        public WavefrontModel LoadStream( Stream stream ) {
            StreamReader reader = new StreamReader( stream );
            List<Vector3> points = new List<Vector3>();
            List<Vector3> normals = new List<Vector3>();
            List<Vector2> texCoords = new List<Vector2>();
            List<int> indices = new List<int>();
            List<Face> faces = new List<Face>();

            string line;
            char[] splitChars = { ' ' };
            while( ( line = reader.ReadLine() ) != null ) {
                line = line.Trim( splitChars );
                line = line.Replace( "  " , " " );

                string[] parameters = line.Split( splitChars );

                switch( parameters[ 0 ] ) {
                    case "p": // Point
                        break;

                    case "v": // Vertex
                        float x = float.Parse( parameters[ 1 ] );
                        float y = float.Parse( parameters[ 2 ] );
                        float z = float.Parse( parameters[ 3 ] );
                        points.Add( new Vector3( x , y , z ) );
                        break;

                    case "vt": // TexCoord
                        float u = float.Parse( parameters[ 1 ] );
                        float v = float.Parse( parameters[ 2 ] );
                        texCoords.Add( new Vector2( u , v ) );
                        break;

                    case "vn": // Normal
                        float nx = float.Parse( parameters[ 1 ] );
                        float ny = float.Parse( parameters[ 2 ] );
                        float nz = float.Parse( parameters[ 3 ] );
                        normals.Add( new Vector3( nx , ny , nz ) );
                        break;

                    case "f":
                        faces.Add( parseFace( parameters ) );
                        break;
                }
            }
            return new WavefrontModel(points,texCoords,normals,faces);
        }

        public WavefrontModel LoadFile( string file ) {
            using( FileStream s = File.Open( file , FileMode.Open ) ) {
                WavefrontModel mesh = LoadStream( s );
                s.Close();
                return mesh;
            }
        }

        private static Face parseFace( string[] indices ) {
            ModelVertex[] p = new ModelVertex[ indices.Length - 1 ];
            for( int i = 0; i < p.Length; i++ ) {
                p[ i ] = parsePoint( indices[ i + 1 ] );
            }
            return new Face( p );
        }

        private static ModelVertex parsePoint( string s ) {
            char[] splitChars = { '/' };
            string[] parameters = s.Split( splitChars );
            int vert = int.Parse( parameters[ 0 ] ) - 1;
            int tex = int.Parse( parameters[ 1 ] ) - 1;
            int norm = int.Parse( parameters[ 2 ] ) - 1;
            return new ModelVertex( vert , norm , tex );
        }
    }
}

我的模型加载如下:

rockMesh = new WavefrontModel( "C:/Users/Krynn/Desktop/RockModel/untitled.obj" );

并且这样渲染:

private void DrawMesh(WavefrontModel m) {

            GL.Enable( EnableCap.Texture2D );
            GL.BindTexture( TextureTarget.Texture2D , rockTexture.GetID() );
            GL.Begin( BeginMode.Triangles );
            foreach( Face f in m.Faces ) {

                foreach( ModelVertex p in f.Points ) {

                    Vector3 v = m.Vertices[ p.Vertex ];
                    Vector3 n = m.Normals[ p.Normal ];
                    Vector2 tc = m.TexCoords[ p.TexCoord ];
                    GL.Vertex3( v.X , v.Y , v.Z );
                    GL.Normal3( n.X , n.Y , n.Z );
                    GL.TexCoord2( tc.Y , tc.X );

                }
            }
            GL.End();
        }

这是Blender生成的模型,我注意到&#34; vt&#34;不是1.0和0.0,这可能是问题吗?

# Blender v2.71 (sub 0) OBJ File: 'untitled.blend'
# www.blender.org
mtllib untitled.mtl
o Cube_Cube.001
v -0.872541 -0.208332 -0.514779
v -0.872541 -0.208332 -2.514779
v 1.127459 -0.208332 -2.514779
v 1.127459 -0.208332 -0.514779
v -0.872541 1.791668 -0.514779
v -0.872541 1.791668 -2.514779
v 1.127459 1.791668 -2.514779
v 1.127459 1.791668 -0.514779
vt 0.999900 0.000100
vt 0.999900 0.999900
vt 0.000100 0.999900
vt 0.000100 0.000100
vn -1.000000 0.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 0.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
usemtl None
s off
f 6/1/1 2/2/1 1/3/1
f 7/1/2 3/2/2 2/3/2
f 8/3/3 4/4/3 3/1/3
f 5/3/4 1/4/4 4/1/4
f 2/4/5 3/1/5 4/2/5
f 7/2/6 6/3/6 5/4/6
f 5/4/1 6/1/1 1/3/1
f 6/4/2 7/1/2 2/3/2
f 7/2/3 8/3/3 3/1/3
f 8/2/4 5/3/4 4/1/4
f 1/3/5 2/4/5 4/2/5
f 8/1/6 7/2/6 5/4/6

另外,这是我的Texture类:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL;
using System.Drawing.Imaging;

namespace GameProject.Game.Framework {
    public class Texture {

        private int textureID;

        public Texture( string texturePath ) {
            Load( texturePath );
        }

        public Texture( Bitmap texture ) {
            LoadInternal( texture );
        }

        private void LoadInternal( Bitmap bitmap ) {
            GL.Enable( EnableCap.Texture2D );
            GL.Hint( HintTarget.PerspectiveCorrectionHint , HintMode.Nicest );
            GL.GenTextures( 1 , out textureID );
            GL.BindTexture( TextureTarget.Texture2D , textureID );
            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 ,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra , PixelType.UnsignedByte , data.Scan0 );
            bitmap.UnlockBits( data );
            GL.BindTexture( TextureTarget.Texture2D , 0 );
        }

        public int GetID() {
            return textureID;
        }

        private void Load( string texturePath ) {
            GL.Enable( EnableCap.Texture2D );
            if( System.IO.File.Exists( texturePath ) ) {
                GL.Enable( EnableCap.Texture2D );
                Bitmap bitmap = new Bitmap( texturePath );
                GL.Hint( HintTarget.PerspectiveCorrectionHint , HintMode.Nicest );
                GL.GenTextures( 1 , out textureID );
                GL.BindTexture( TextureTarget.Texture2D , textureID );
                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 ,
                    OpenTK.Graphics.OpenGL.PixelFormat.Bgra , PixelType.UnsignedByte , data.Scan0 );
                bitmap.UnlockBits( data );
            }
        }
    }
}

关卡中没有其他硬编码几何体有这个问题;几何本身也是几千个体素。除模型外的所有东西都有正确的纹理坐标。

1 个答案:

答案 0 :(得分:4)

在传统OpenGL中使用立即模式,glVertex*()调用使用当时设置的其他顶点属性(例如,正常,颜色)的值发出新顶点。

在发布的代码中,调用的顺序是:

GL.Vertex3( v.X , v.Y , v.Z );
GL.Normal3( n.X , n.Y , n.Z );
GL.TexCoord2( tc.Y , tc.X );

由于Vertex3调用首先出现,它将使用最近设置的法线坐标和纹理坐标,它们是前一个顶点的值。然后Normal3TexCoord2调用会更改这些属性的当前值,但新值只会用于下一次Vertex3()调用。

要为顶点使用指定的法线和纹理坐标,Vertex3()调用必须是最后一次:

GL.Normal3( n.X , n.Y , n.Z );
GL.TexCoord2( tc.Y , tc.X );
GL.Vertex3( v.X , v.Y , v.Z );