Форум программистов, компьютерный форум, киберфорум
8Observer8
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

Загрузка текстурированно­й 3D модели из OBJ-формата в WPF, C#, OpenGL 3.3

Запись от 8Observer8 размещена 26.04.2020 в 19:16
Показов 4240 Комментарии 0
Метки .net, c#, gamedev, opengl, wpf

Содержание блога

Скачать исходники: 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);
}
Миниатюры
Нажмите на изображение для увеличения
Название: Export.png
Просмотров: 1302
Размер:	23.1 Кб
ID:	6187  
Изображения
 
Вложения
Тип файла: zip ObjLoader_OpenGL33WPF.zip (5.46 Мб, 1802 просмотров)
Метки .net, c#, gamedev, opengl, wpf
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Новые блоги и статьи
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
Как ИИ начал спорить и врать (возможно почуяв опасность для себя от индустрии - уход от электроники).
Hrethgir 04.08.2026
Недельный диалог, на фоне событий с НПЗ. Да, из спирта можно получать бензин, и это не сложно. Но потом в схеме я решил избавиться от насоса, при этом полностью сделав контроль подачи спирта в. . .
Термопринтер QR701
Argus19 03.08.2026
Термопринтер QR701 Купил два термопринтера QR701. На сэлф-тесте написано: Language: PC936 (GB18030). Что означает, что принтеры могут печатать только латиницу и китайские иероглифы. Так же. . .
Создание формы заимствованного документа
Maks 03.08.2026
Задача: Необходимо создать собственную форму заимствованного документа. На форме должен быть реквизит "Покупатель", а также табличная часть со следующими реквизитами: - Расчетный счет покупателя. . .
Задача предоставления скидок покупателям
Maks 03.08.2026
Задача: В документе "Продажи" необходимо реализовать функционал предоставления скидок покупателям. Скидка должна автоматически рассчитываться и подставляться в соответствующее поле при выборе. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru