Содержание блога
Скачать исходники: ObjLoader_OpenGL33WPF.zip (5.46 Мб)
К записи прикреплён скриншот экспорта из Blender. Должна стоять галочка, что модель будет триангулирована.
MainWindow.xaml
| XML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <Window x:Class="ObjLoader_OpenGL33WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ObjLoader_OpenGL33WPF"
xmlns:opentk="clr-namespace:OpenTK;assembly=OpenTK.GLControl"
mc:Ignorable="d"
Title="ObjLoader" Height="250" Width="268">
<Grid>
<WindowsFormsHost Initialized="WindowsFormsHost_Initialized">
<opentk:GLControl x:Name="glControl" Load="GLControl_Load" Paint="GLControl_Paint" Resize="GLControl_Resize" />
</WindowsFormsHost>
</Grid>
</Window> |
|
MainWindow.xaml.css
| C# | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
| using System;
using System.Windows;
using OpenTK.Graphics.OpenGL;
using System.Drawing;
using System.Drawing.Imaging;
using OpenTK;
using System.Windows.Threading;
namespace ObjLoader_OpenGL33WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool _canDraw = false;
private int _numOfVertices;
private ObjLoader _obj;
private float _angle = 0.0f;
private int _program;
private int _uMvpMatrix;
private int _uModelMatrix;
private Matrix4 _mvpMatrix;
private Matrix4 _modelMatrix;
private Matrix4 _viewMatrix;
private Matrix4 _projMatrix;
private DispatcherTimer _dispatcherTimer;
public MainWindow()
{
InitializeComponent();
}
private void WindowsFormsHost_Initialized(object sender, EventArgs e)
{
glControl.MakeCurrent();
}
private void GLControl_Load(object sender, EventArgs e)
{
GL.ClearColor(0.2f, 0.3f, 0.2f, 1.0f);
GL.Enable(EnableCap.DepthTest);
if (!ShaderHelpers.InitShaders(
"Shaders/vert.shader.glsl",
"Shaders/frag.shader.glsl", out _program))
{
return;
}
if (!InitVertexBuffers(_program))
{
return;
}
InitTextures();
_uMvpMatrix = GL.GetUniformLocation(_program, "uMvpMatrix");
if (_uMvpMatrix < 0)
{
MessageBox.Show("Failed to get uMvpMatrix variable.");
return;
}
_uModelMatrix = GL.GetUniformLocation(_program, "uModelMatrix");
if (_uModelMatrix < 0)
{
MessageBox.Show("Failed to get uModelMatrix variable.");
return;
}
_viewMatrix = Matrix4.LookAt(
new Vector3(0.0f, 3.0f, 10.0f),
new Vector3(0.0f, 0.0f, 0.0f),
new Vector3(0.0f, 1.0f, 0.0f));
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick += OnUpdate;
_dispatcherTimer.Interval = TimeSpan.FromMilliseconds(16.0);
_dispatcherTimer.Start();
_canDraw = true;
}
private void OnUpdate(object sender, EventArgs e)
{
_angle += 2.0f;
_modelMatrix =
Matrix4.CreateScale(3.0f) *
Matrix4.CreateRotationY(MathHelper.DegreesToRadians(_angle));
_mvpMatrix = _modelMatrix * _viewMatrix * _projMatrix;
GL.UniformMatrix4(_uMvpMatrix, false, ref _mvpMatrix);
GL.UniformMatrix4(_uModelMatrix, false, ref _modelMatrix);
glControl.Invalidate();
}
private void GLControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
GL.Viewport(0, 0, glControl.Width, glControl.Height);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
if (_canDraw)
{
GL.DrawArrays(PrimitiveType.Triangles, 0, _numOfVertices);
}
glControl.SwapBuffers();
}
private void GLControl_Resize(object sender, EventArgs e)
{
SetProjMatrix();
glControl.Invalidate();
}
private bool InitVertexBuffers(int program)
{
_obj = new ObjLoader();
_obj.LoadModel("Models/monkey/monkey.obj");
_numOfVertices = _obj.vertIndex.Count;
int vbo;
GL.GenBuffers(1, out vbo);
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.BufferData(BufferTarget.ArrayBuffer,
_obj.model.Length * sizeof(float),
_obj.model, BufferUsageHint.StaticDraw);
int textureOffset = _obj.vertIndex.Count * 3 * sizeof(float);
int normalOffset = textureOffset + _obj.texIndex.Count * 2 * sizeof(float);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 0, 0);
GL.EnableVertexAttribArray(0);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 0, textureOffset);
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(2, 3, VertexAttribPointerType.Float, false, 0, normalOffset);
GL.EnableVertexAttribArray(2);
return true;
}
private void InitTextures()
{
int texture;
GL.GenTextures(1, out texture);
GL.BindTexture(TextureTarget.Texture2D, texture);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.Repeat);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.Repeat);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
string imageFileName = "Models/monkey/DefTexture.png";
Bitmap image;
try
{
image = new Bitmap(imageFileName);
}
catch (Exception)
{
MessageBox.Show("Failed to load the texture: " + imageFileName);
return;
}
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
BitmapData data = image.LockBits(rect, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.Rgb, image.Width, image.Height,
0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba,
PixelType.UnsignedByte, data.Scan0);
image.UnlockBits(data);
}
private void SetProjMatrix()
{
_projMatrix = Matrix4.CreatePerspectiveFieldOfView(
MathHelper.DegreesToRadians(45.0f),
(float)glControl.Width / glControl.Height, 0.1f, 1000.0f);
}
}
} |
|
ObjLoader.cs
| C# | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
| using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Globalization;
namespace ObjLoader_OpenGL33WPF
{
class ObjLoader
{
public float[] model;
public List<float> vertCoord = new List<float>();
public List<float> texCoord = new List<float>();
public List<int> vertIndex = new List<int>();
public List<int> texIndex = new List<int>();
public List<int> normIndex = new List<int>();
private List<float> _model = new List<float>();
private List<List<int>> _vertIndex = new List<List<int>>();
private List<List<int>> _texIndex = new List<List<int>>();
private List<List<int>> _normIndex = new List<List<int>>();
private List<List<float>> _vertCoords = new List<List<float>>();
private List<List<float>> _texCoords = new List<List<float>>();
private List<List<float>> _normCoords = new List<List<float>>();
private const string WHITESPACE_RE = @"\s+";
public void LoadModel(string filePath)
{
if (!File.Exists(filePath))
{
MessageBox.Show("Failed to open the file: " + filePath);
return;
}
using (var sr = new StreamReader(filePath))
{
string data = sr.ReadToEnd();
string[] lines = data.Split(new char[] { '\n' });
CultureInfo culture = new CultureInfo("en-US");
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].StartsWith("#")) continue;
string line = lines[i].Trim();
List<string> values = new List<string>(Regex.Split(line, WHITESPACE_RE));
if (values.Count == 0) continue;
if (values[0] == "v")
{
float x = float.Parse(values[1], culture);
float y = float.Parse(values[2], culture);
float z = float.Parse(values[3], culture);
_vertCoords.Add(new List<float>() { x, y, z });
}
if (values[0] == "vt")
{
float u = float.Parse(values[1], culture);
float v = float.Parse(values[2], culture);
_texCoords.Add(new List<float>() { u, v });
}
if (values[0] == "vn")
{
float x = float.Parse(values[1], culture);
float y = float.Parse(values[2], culture);
float z = float.Parse(values[3], culture);
_normCoords.Add(new List<float>() { x, y, z });
}
if (values[0] == "f")
{
List<int> face_i = new List<int>();
List<int> tex_i = new List<int>();
List<int> norm_i = new List<int>();
for (int j = 1; j < 4; j++)
{
string[] w = values[j].Split(new char[] { '/' });
face_i.Add(int.Parse(w[0]) - 1);
tex_i.Add(int.Parse(w[1]) - 1);
norm_i.Add(int.Parse(w[2]) - 1);
}
_vertIndex.Add(face_i);
_texIndex.Add(tex_i);
_normIndex.Add(norm_i);
}
}
for (int i = 0; i < _vertIndex.Count; i++)
{
for (int j = 0; j < _vertIndex[i].Count; j++)
{
vertIndex.Add(_vertIndex[i][j]);
}
}
for (int i = 0; i < _texIndex.Count; i++)
{
for (int j = 0; j < _texIndex[i].Count; j++)
{
texIndex.Add(_texIndex[i][j]);
}
}
for (int i = 0; i < _normIndex.Count; i++)
{
for (int j = 0; j < _normIndex[i].Count; j++)
{
normIndex.Add(_normIndex[i][j]);
}
}
for (int i = 0; i < vertIndex.Count; i++)
{
int index = vertIndex[i];
List<float> coords = _vertCoords[index];
for (int j = 0; j < coords.Count; j++)
{
_model.Add(coords[j]);
vertCoord.Add(coords[j]);
}
}
for (int i = 0; i < texIndex.Count; i++)
{
int index = texIndex[i];
List<float> coords = _texCoords[index];
for (int j = 0; j < coords.Count; j++)
{
_model.Add(coords[j]);
texCoord.Add(coords[j]);
}
}
for (int i = 0; i < normIndex.Count; i++)
{
int index = normIndex[i];
List<float> coords = _normCoords[index];
for (int j = 0; j < coords.Count; j++)
{
_model.Add(coords[j]);
}
}
model = _model.ToArray();
}
}
}
} |
|
ShaderHelpers.cs
| C# | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
| using System;
using OpenTK.Graphics.OpenGL;
using System.Windows.Forms;
using System.IO;
namespace ObjLoader_OpenGL33WPF
{
class ShaderHelpers
{
///<summary>
///Create a program object and make current
///</summary>
///<param name="vShader">a vertex shader program</param>
///<param name="fShader">a fragment shader program</param>
///<param name="program">created program</param>
///<returns>
///return true, if the program object was created and successfully made current
///</returns>
public static bool InitShaders(string vShaderPath, string fShaderPath, out int program)
{
string vShaderSource, fShaderSource;
LoadShaderFromFile(vShaderPath, out vShaderSource);
LoadShaderFromFile(fShaderPath, out fShaderSource);
program = CreateProgram(vShaderSource, fShaderSource);
if (program == 0)
{
MessageBox.Show("Failed to create program");
return false;
}
GL.UseProgram(program);
return true;
}
private static int CreateProgram(string vShader, string fShader)
{
// Create shader object
int vertexShader = CreateShader(ShaderType.VertexShader, vShader);
int fragmentShader = CreateShader(ShaderType.FragmentShader, fShader);
if (vertexShader == 0 || fragmentShader == 0)
{
return 0;
}
// Create a program object
int program = GL.CreateProgram();
if (program == 0)
{
return 0;
}
// Attach the shader objects
GL.AttachShader(program, vertexShader);
GL.AttachShader(program, fragmentShader);
// Link the program object
GL.LinkProgram(program);
// Check the result of linking
int status;
GL.GetProgram(program, GetProgramParameterName.LinkStatus, out status);
if (status == 0)
{
string errorString = string.Format("Failed to link program: {0}" + Environment.NewLine, GL.GetProgramInfoLog(program));
MessageBox.Show(errorString);
GL.DeleteProgram(program);
GL.DeleteShader(vertexShader);
GL.DeleteShader(fragmentShader);
return 0;
}
return program;
}
///<summary>
///Load a shader from a file
///</summary>
///<param name="errorOutputFileName">a file name for error messages</param>
///<param name="fileName">a file name to a shader</param>
///<param name="shaderSource">a shader source string</param>
public static void LoadShaderFromFile(string shaderFileName, out string shaderSource)
{
shaderSource = null;
using (StreamReader sr = new StreamReader(shaderFileName))
{
shaderSource = sr.ReadToEnd();
}
}
private static int CreateShader(ShaderType shaderType, string shaderSource)
{
// Create shader object
int shader = GL.CreateShader(shaderType);
if (shader == 0)
{
MessageBox.Show("Unable to create shader");
return 0;
}
// Set the shader program
GL.ShaderSource(shader, shaderSource);
// Compile the shader
GL.CompileShader(shader);
// Check the result of compilation
int status;
GL.GetShader(shader, ShaderParameter.CompileStatus, out status);
if (status == 0)
{
string errorString = string.Format("Failed to compile {0} shader: {1}", shaderType.ToString(), GL.GetShaderInfoLog(shader));
MessageBox.Show(errorString);
GL.DeleteShader(shader);
return 0;
}
return shader;
}
}
} |
|
vert.shader.glsl
| glSlang | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| #version 330
layout(location=0) in vec3 aPosition;
layout(location=1) in vec2 aTexCoord;
layout(location = 2) in vec3 aNormal;
uniform mat4 uMvpMatrix;
uniform mat4 uModelMatrix;
out vec2 vTexCoord;
out vec3 vNormal;
void main()
{
gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
vNormal = (uModelMatrix * vec4(aNormal, 0.0)).xyz;
vTexCoord = aTexCoord;
} |
|
frag.shader.glsl
| glSlang | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #version 330
precision mediump float;
uniform sampler2D uSampler;
in vec2 vTexCoord;
in vec3 vNormal;
out vec4 fragColor;
void main()
{
vec3 ambientLightIntensity = vec3(0.2, 0.2, 0.2);
vec3 sunLightIntensity = vec3(0.7, 0.7, 0.7);
vec3 sunLightDirection = normalize(vec3(-20.0, 20.0, 20.0));
vec2 flipped = vec2(vTexCoord.x, 1 - vTexCoord.y);
vec4 texel = texture(uSampler, flipped);
vec3 lightIntensity = ambientLightIntensity + sunLightIntensity * max(dot(vNormal, sunLightDirection), 0.0f);
fragColor = vec4(texel.rgb * lightIntensity, texel.a);
} |
|
|