我决定在我的OpenTK C#游戏中实现一个模型加载器;除了纹理坐标完全奇怪之外,它呈现得很好。我使用的代码是我在互联网上找到的OpenTK模型加载器的略微修改版本。我已经尝试过在搅拌机中解开我的模型,但似乎结果总是一样紧张。
以下是我的模型在Blender中的样子:
这就是我游戏中的样子:
这是我的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 );
}
}
}
}
关卡中没有其他硬编码几何体有这个问题;几何本身也是几千个体素。除模型外的所有东西都有正确的纹理坐标。
答案 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
调用首先出现,它将使用最近设置的法线坐标和纹理坐标,它们是前一个顶点的值。然后Normal3
和TexCoord2
调用会更改这些属性的当前值,但新值只会用于下一次Vertex3()
调用。
要为此顶点使用指定的法线和纹理坐标,Vertex3()
调用必须是最后一次:
GL.Normal3( n.X , n.Y , n.Z );
GL.TexCoord2( tc.Y , tc.X );
GL.Vertex3( v.X , v.Y , v.Z );