Помню как в 2010м году нашел на этом сайте классные уроки по C#+OpenGL и познакомился с интересными людьми, с которыми мы провели много времени в TeamSpeak и Skype, создавая игры. Я писал в блог какие-то школо-статьи с грамматическими ошибками и сообщество сайта оказывало поддержку: Isaer, Anvi и Bumblebee). Эти статьи даже сейчас не удалили:D.

В школьные времена было крайне интересно разрабатывать игры с нуля, разбираться в тонкостях игровых движков. Повзрослев, романтика пропала. Отличные игровые движки уже всё сделали за нас, писать свой движок не имеет смысла. Игровые студии нацелены на получение денег. Даже свои идеи начать разработку чего-то теперь проходят фильтр прибыльности.

Случайно найдя этот сайт, я удивился, как далеко со временем я ушел от гемдева. По окончанию школы я фрилансил в js/php, чтобы получать какие-то деньги, всё ещё думая о будущем в геймдеве. JavaScript открыл мне Canvas, WebRTC, WebGL. Я продолжал делать демки, а теперь эти технологии полностью вытеснили Flash. Позже в поисках серьёзной работы, я надеялся найти что-то в гемдеве и моим тестовым заданием было сделать однорукого бандита из казино:|. Тогда удалось найти оффер поперспективнее и я начал дальше углубляться в веб и бекенд. Окунулся в мир распределённых систем.

Некоторые мои друзья сейчас работают в геймдеве. Не знаю почему так происходит, но их работа - это постоянный стресс (вечные дедлайны и правки). Я чувствую, что судьба помогла мне найти лучший для себя путь.

Опыт с геймдевом стал хорошей базой: практика работы с языком программирования, структуры и алгоритмы, умение дебажить и танцевать с бубном (это вообще самый важный скилл).

С особой теплотой вспоминаю как в 2010 году мы пробовали создавать игры (https://esate.ru/blogs/Bumblebee/4065/). Спасибо сообществу Esate

Прокрастинатор приветствует!
Это первая статья из цикла по разработке мультиплатформенной игры на языке программирования C# с использованием библиотеки OpenTK, которая предоставляет простой доступ к OpenGL. Так же библиотека берет на себя работу с окнами приложения и, таким образом, упрощает нам жизнь при разработке не только под windows, но и linux и mac системы.
В дополнение к OpenTK мы будем использовать библиотеки Assimp.Net (порт библиотеки Assimp для С++) для загрузки трехмерных моделей, BEPUphysics для реализации физики и SixLabors для работы с двумерной графикой.

Для начала этого набора нам вполне хватит. Все библиотеки устанавливаются с помощью NuGet и, чуть позже, мы этого коснемся.
В качестве текстового редактора я буду использовать Visual Studio Code так как он доступен на всех целевых платформах, бесплатен и его функционала вполне достаточно.
Перед тем, как мы начнем, нужно установить SDK для работы с .Net Core. Сделать это можно по это ссылке: https://dotnet.microsoft.com/download Нас интересует LTS версия .Net Core и на момент написания статьи это 3.1. Выбираем ее и устанавливаем. После установки пишем в терминале dotnet и убеждаемся, что все установлено верно. Если же нет - решите вопрос и возвращайтесь к статье когда все будет в порядке.

Если вы, как и я, используете линукс, то быстрее и надежнее будет установить SDK через ваш пакетный менеджер, но убедитесь в том, что вы устанавливаете именно 3.1 версию.
Со вступлением покончено, можно приступать к самому интересному.
Что бы создать проект нужно проделать несколько простых действий.
Для начала, создадим папку, в которой будут храниться все файлы будущей игры.
Далее нужно выполнить команду, которая создаст каркас консольного приложения для нас.

dotnet new console

Если вы корректно установили .Net Core, то через несколько секунд программа сообщит вам, что файлы успешно созданы и можно переходить к следующему шагу.
Запускаем VS Code и открываем недавно созданную папку.



Скорее всего, IDE сама предложит установить все необходимое для комфортной работы, но если этого не произошло, то вам нужно вот это:
https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp

Следующая наша задача - установка библиотек из NuGet

Делается это довольно просто.
Переходим на сайт https://www.nuget.org/ и в строке поиска находим все необходимое.
Для упрощения вам жизни я просто опубликую команды, которые нужно ввести в консоли(которую можно вызвать прямо в VS Code):

dotnet add package OpenTK --version 4.6.7
dotnet add package BepuPhysics --version 2.3.0
dotnet add package AssimpNet --version 5.0.0-beta1
dotnet add package SixLabors.Fonts --version 1.0.0-beta15
dotnet add package SixLabors.ImageSharp --version 1.0.3

dotnet add package SixLabors.ImageSharp.Drawing --version 1.0.0-beta13

Версии библиотек актуальны на момент написания статьи. В будущем, когда мы будем обновлять библиотеки, на этом будет акцентировано дополнительное внимание
Хорошо. Еще одна настройка перед тем как мы начнем писать код, для того, что бы папка с ресурсами (assets) со всем ее содержимым копировалась при компиляции в папку с исполняемым файлом.
Откройте файл с расширением .csproj и вы найдете там тег ItemGroup. В него нужно добавить код, выделенный отступами.
<ItemGroup>

<Content Include="assets\**\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

<PackageReference Include="AssimpNet" Version="5.0.0-beta1" />
<PackageReference Include="BepuPhysics" Version="2.3.0" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta15" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.3" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta13" />
</ItemGroup>
Осталось только создать саму папку "assets" в корне нашего проекта и базовая инициализация на этом закончена.

Спасибо за внимание
В продолжение перевода с С++ на C#/OpenTk, привожу вторую и последнюю часть цикла уроков.
В прошлой заметке всё было усыпано скриншотами, о том как создать и настроить проект, а далее приводился лишь кусок кода. В этот раз будет по большей части код.
Основной момент, состоит в том, что вместо GLM из плюсового кода мы используем возможности OpenTk для работы с матрицами.

Код:
using System;
using System.Drawing;
using System.IO;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

namespace openTk2
{
 public class GlShader : IDisposable
 {
 int ShaderProgram;
 int vertex_shader;
 int fragment_shader;
 
 private void ReleaseUnmanagedResources()
 {
 GL.UseProgram(0);
 GL.DeleteShader(vertex_shader);
 GL.DeleteShader(fragment_shader);
 GL.DeleteProgram(ShaderProgram);
 }
//Освобождение ресурсов шейдера
 public void Dispose()
 {
 ReleaseUnmanagedResources();
 GC.SuppressFinalize(this);
 }

 ~GlShader()
 {
 ReleaseUnmanagedResources();
 }

 public int loadFiles(string vertex_file_name, string fragment_file_name)
 {
 vertex_shader = loadSourcefile(vertex_file_name, ShaderType.VertexShader);
 fragment_shader = loadSourcefile(fragment_file_name, ShaderType.FragmentShader);

 linkProgram();

 return ShaderProgram;
 }

 private int loadSourcefile(string file_name, ShaderType type)
 {
 var source = File.ReadAllText(file_name);
 return compileSource(source, type);
 }

 public int load(string vertex_source, string fragment_source)
 {
 vertex_shader = compileSource(vertex_source, ShaderType.VertexShader);
 fragment_shader = compileSource(fragment_source, ShaderType.FragmentShader);

 linkProgram();
 return ShaderProgram;
 }

 private int compileSource(string source, ShaderType type)
 {
 //! Создаем фрагментный шейдер
 int shader = GL.CreateShader(type);
 //! Передаем исходный код
 GL.ShaderSource(shader, source);
 //! Компилируем шейдер
 GL.CompileShader(shader);
 return shader;
 }

 //! Функция печати лога шейдера
 private static void ShaderLog(string tag, int shader)
 {
 var infoLog = "";

 GL.GetShaderInfoLog(shader, out infoLog);

 if (infoLog.Length > 1)
 Console.WriteLine(tag + " InfoLog: " + infoLog + "\n\n\n");
 }

 private void linkProgram()
 {
 if (vertex_shader == 0 || fragment_shader == 0)
 {
 ShaderProgram = 0;
 return;
 }

 ShaderProgram = GL.CreateProgram();
 GL.AttachShader(ShaderProgram, vertex_shader);
 GL.AttachShader(ShaderProgram, fragment_shader);

 GL.LinkProgram(ShaderProgram);
 string log = GL.GetProgramInfoLog(ShaderProgram);
 if (string.IsNullOrEmpty(log) == false)
 Console.WriteLine(log);
 }

 public void use()
 {
 GL.UseProgram(ShaderProgram);
 }

 //---------------------------------------------------

 //! Attribute get
 public int getAttribLocation(string name)
 {
 int location = -1;
 location = GL.GetAttribLocation(ShaderProgram, name);
 if (location == -1)
 Console.WriteLine("Could not bind attribute {0}", name); 

 return location;
 }
 
 //! Attribute get
 public int getUniformLocation(string name)
 {
 int location = -1;
 location = GL.GetUniformLocation(ShaderProgram, name);
 if (location == -1)
 Console.WriteLine("Could not bind attribute {0}", name); 

 return location;
 }

 public void setUniform(int unifMatrix, Matrix4 matrixProjection)
 {
 GL.UniformMatrix4(unifMatrix, false, ref matrixProjection);
 }
 }

 class Game : GameWindow
 {
 private GlShader shader = new GlShader();

 //! ID атрибута вершин
 int Attrib_vertex;
 //! ID атрибута цветов
 int Attrib_color;
 //! ID юниформ матрицы проекции
 int Unif_matrix;
 //! ID Vertex Buffer Object
 int VBO_vertex;
 //! ID Vertex Buffer Object
 int VBO_color;
 //! ID VBO for element indices
 int VBO_element;

 //! Вершина
 public struct Vertex
 {
 public float x;
 public float y;
 public float z;
 };
 
 //! Количество индексов
 int Indices_count;
 //! Матрица проекции
 Matrix4 Matrix_projection;

 public Game()
 : base(640, 480, new GraphicsMode(new ColorFormat(32), 16), "Simple shaders on OpenTK!")
 {

 }

 protected override void OnLoad(EventArgs e)
 {
 base.OnLoad(e);

 GL.ClearColor(Color.Black);

 //! Инициализация
 initGL();
 initVBO();
 initShader();
 }

 protected override void OnUnload(EventArgs e)
 {
 base.OnUnload(e);

 //! Освобождение ресурсов
 freeVBO();
 shader.Dispose();
 }

 //! Функция печати лога шейдера
 static void ShaderLog(string tag, int shader)
 {
 string infoLog;

 GL.GetShaderInfoLog(shader, out infoLog);

 if (infoLog.Length > 1)
 Console.WriteLine(tag + " InfoLog: " + infoLog + "\n\n\n");
 }

 //! Проверка ошибок OpenGL, если есть то вывод в консоль тип ошибки
 static void CheckOpenGLerror()
 {
 ErrorCode errCode = GL.GetError();
 if (errCode != ErrorCode.NoError)
 {
 Console.WriteLine("OpenGl error! - {0}", errCode);
 Console.WriteLine(System.Environment.StackTrace);
 }
 }
 
 void initGL()
 {
 GL.ClearColor(0, 0, 0, 0);
 GL.Enable(EnableCap.DepthTest);
 }

 //! Инициализация шейдеров
 private void initShader()
 {
 //! Исходный код шейдеров
 const string vsSource = 
 @"attribute vec3 coord;
 attribute vec3 color;
 varying vec3 var_color;
 uniform mat4 matrix;
 void main() {
 gl_Position = matrix * vec4(coord, 1.0);
 var_color = color;
 }";
 const string fsSource = 
 @"varying vec3 var_color;
 void main() {
 gl_FragColor = vec4(var_color, 1.0);
 }";
 
 if(shader.load(vsSource, fsSource) == -1)
 {
 Console.WriteLine("error load shader");
 return;
 }
 
 CheckOpenGLerror();

 //! Вытягиваем ID атрибута из собранной программы 
 Attrib_vertex = shader.getAttribLocation("coord");
 
 CheckOpenGLerror();

 //! Вытягиваем ID юниформ
 Attrib_color = shader.getAttribLocation("color");
 
 CheckOpenGLerror();

 //! Вытягиваем ID юниформ матрицы проекции
 Unif_matrix = shader.getUniformLocation("matrix");

 CheckOpenGLerror();
 }

//! Инициализация VBO_vertex
 void initVBO()
 {
 //! Вершины куба
 Vertex [] vertices = { 
 new Vertex {x = -1.0f,y = -1.0f,z = -1.0f},
 new Vertex {x = 1.0f, y =-1.0f, z =-1.0f},
 new Vertex {x = 1.0f, y = 1.0f, z =-1.0f},
 new Vertex {x = -1.0f,y = 1.0f, z =-1.0f},
 new Vertex {x = -1.0f,y = -1.0f,z = 1.0f},
 new Vertex {x = 1.0f, y =-1.0f, z = 1.0f},
 new Vertex {x = 1.0f, y = 1.0f, z = 1.0f},
 new Vertex {x = -1.0f,y = 1.0f,z = 1.0f}
 };
 //! Цвета куба без альфа компонента(RGB)
 Vertex [] colors = { 
 new Vertex {x = 1.0f,y = 0.5f,z = 1.0f},
 new Vertex {x = 1.0f,y = 0.5f,z = 0.5f},
 new Vertex {x = 0.5f,y = 0.5f,z = 1.0f},
 new Vertex {x = 0.0f,y = 1.0f,z = 1.0f},
 new Vertex {x = 1.0f,y = 0.0f,z = 1.0f},
 new Vertex {x = 1.0f,y = 1.0f,z = 0.0f},
 new Vertex {x = 1.0f,y = 0.0f,z = 1.0f},
 new Vertex {x = 0.0f,y = 1.0f,z = 1.0f}
 };
 //! Индексы вершин, общие и для цветов
 uint [] indices = {
 0, 4, 5, 0, 5, 1,
 1, 5, 6, 1, 6, 2,
 2, 6, 7, 2, 7, 3,
 3, 7, 4, 3, 4, 0,
 4, 7, 6, 4, 6, 5,
 3, 0, 1, 3, 1, 2
 };

 // Создаем буфер для вершин
 GL.GenBuffers(1, out VBO_vertex);
 GL.BindBuffer(BufferTarget.ArrayBuffer, VBO_vertex);
 GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * Vector3.SizeInBytes, vertices, BufferUsageHint.StaticDraw);

 // Создаем буфер для цветов вершин
 GL.GenBuffers(1, out VBO_color);
 GL.BindBuffer(BufferTarget.ArrayBuffer, VBO_color);
 GL.BufferData(BufferTarget.ArrayBuffer, colors.Length * Vector3.SizeInBytes, colors, BufferUsageHint.StaticDraw);

 // Создаем буфер для индексов вершин
 GL.GenBuffers(1, out VBO_element);
 GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBO_element);
 GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(int), indices, BufferUsageHint.StaticDraw);

 Indices_count = indices.Length;

 CheckOpenGLerror();
 }

 void freeVBO()
 {
 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
 GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
 
 GL.DeleteBuffers(1, ref VBO_element);
 GL.DeleteBuffers(1, ref VBO_element);
 GL.DeleteBuffers(1, ref VBO_color);
 }

 protected override void OnRenderFrame(FrameEventArgs e)
 {
 base.OnRenderFrame(e);

 GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

 //! Устанавливаем шейдерную программу текущей
 shader.use();
 //! Передаем матрицу в шейдер
 shader.setUniform(Unif_matrix, Matrix_projection);
 
 //! Подключаем буфер с индексами вершин общий для цветов и их вершин
 GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBO_element);

 //! ВЕРШИНЫ
 //! Включаем массив атрибутов для вершин
 GL.EnableVertexAttribArray(Attrib_vertex);
 //! Подключаем VBO
 GL.BindBuffer(BufferTarget.ArrayBuffer, VBO_vertex);
 //! Указывая pointer 0 при подключенном буфере, мы указываем что данные в VBO
 GL.VertexAttribPointer(Attrib_vertex, 3, VertexAttribPointerType.Float, false, 0, 0);

 //! ЦВЕТА
 //! Включаем массив атрибутов для цветов
 GL.EnableVertexAttribArray(Attrib_color);
 GL.BindBuffer(BufferTarget.ArrayBuffer, VBO_color);
 GL.VertexAttribPointer(Attrib_color, 3, VertexAttribPointerType.Float, false, 0, 0);


 //! Передаем данные на видеокарту(рисуем)
 GL.DrawElements(BeginMode.Triangles, Indices_count, DrawElementsType.UnsignedInt, 0);

 //! Отключаем массив атрибутов
 GL.DisableVertexAttribArray(Attrib_vertex);

 //! Отключаем массив атрибутов
 GL.DisableVertexAttribArray(Attrib_color);

 CheckOpenGLerror();

 SwapBuffers();
 }

 protected override void OnResize(EventArgs e)
 {
 base.OnResize(e);

 GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);

 var height = ClientRectangle.Height > 0 ? ClientRectangle.Height : 1;
 float aspectRatio = (float)ClientRectangle.Width/(float)height;

 Matrix_projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(45.0f), aspectRatio, 1.0f, 200.0f);
 
 // Перемещаем центр нашей оси координат для того чтобы увидеть куб
 Matrix_projection = Matrix4.Mult(Matrix4.CreateTranslation(new Vector3(0.0f, 0.0f, -10.0f)), Matrix_projection);
 // Поворачиваем ось координат(тоесть весь мир), чтобы развернуть отрисованное
 Matrix_projection = Matrix4.Mult(Matrix4.CreateFromAxisAngle(new Vector3(1.0f, 1.0f, 0.0f), MathHelper.DegreesToRadians(60.0f)), Matrix_projection);
 }
 }

 class Program
 {
 static void Main(string[] args)
 {
 var g = new Game();
 g.Run(30.0f);
 }
 }
}

Этим кодом можно заменить код из прошлой заметки, он должен компилироваться также хорошо.
C# вариант класса для шейдеров реализует интерфейс IDisposable для правильного освобождения ресурсов шейдерной программы. Освобождение происходит по событию выгрузки класса Game OnUnload.
Для трансформации и поворота матриц применяются методы из класса Matrix4, которые есть в OpenTk.

Результат работы:


Впрочем он должен быть такой же, как в оригинальном плюсовом коде.

Ссылка на оригинал.
Когда-то я ознакомился с уроком "Простой шейдер на GLSL", и это вдохновило на изучение шейдеров в OpenGL и сам современный OpenGL в частности. В основном я работаю с C# поэтому после ознакомления с кодом на C++ и его сборкой, приступил к переводу его в С#. Эта заметка как раз о том, как перенести код из урока на C#. Для доступа к OpenGL будет использоваться современная версия враппера OpenTK.

И так сперва создадим консольный проект на C#:
SimpleShaderOpenTk1.PNG

Подключим OpenTk через nuget
SimpleShaderOpenTk1_1.PNG

SimpleShaderOpenTk1_2.PNG

SimpleShaderOpenTk1_4.PNG
если не нашло OpenTk то нужно включить репоизоторий в настройках:
SimpleShaderOpenTk1_3.PNG

Используем следующий код:


using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

namespace openTk1
{
 class Game : GameWindow
 {

 //! Переменные с индентификаторами ID
 //! ID шейдерной программы
 int Program;
 //! ID атрибута
 int Attrib_vertex;
 //! ID юниформ переменной цвета
 int Unif_color;
 //! ID Vertex Buffer Object
 int VBO;

 //! Вершина
 struct Vertex
 {
 public float x;
 public float y;
 };

 public Game()
 : base(640, 480, new GraphicsMode(new ColorFormat(32), 16), "Simple shaders on OpenTK!")
 {

 }

 protected override void OnLoad(EventArgs e)
 {
 base.OnLoad(e);

 GL.ClearColor(Color.Black);

 //! Инициализация
 InitVBO();
 InitShader();
 }

 protected override void OnUnload(EventArgs e)
 {
 base.OnUnload(e);

 //! Освобождение ресурсов
 FreeShader();
 FreeVBO();
 }

 //! Функция печати лога шейдера
 static void ShaderLog(string tag, int shader)
 {
 string infoLog;

 GL.GetShaderInfoLog(shader, out infoLog);

 if (infoLog.Length > 1)
 Console.WriteLine(tag + " InfoLog: " + infoLog + "\n\n\n");
 }

 //! Проверка ошибок OpenGL, если есть то вывод в консоль тип ошибки
 static void CheckOpenGLerror()
 {
 ErrorCode errCode = GL.GetError();
 if (errCode != ErrorCode.NoError)
 Console.WriteLine("OpenGl error! - {0}", errCode);
 }

 private void InitShader()
 {
 //! Исходный код шейдеров
 const string vsSource = @"attribute vec2 coord;
 void main() {
 gl_Position = vec4(coord, 0.0, 1.0);
 };";
 const string fsSource = @"uniform vec4 color;
 void main() {
 gl_FragColor = color;
 }";

 //! Переменные для хранения идентификаторов шейдеров
 int vShader, fShader;

 //! Создаем вершинный шейдер
 vShader = GL.CreateShader(ShaderType.VertexShader);
 //! Передаем исходный код
 GL.ShaderSource(vShader, vsSource);
 //! Компилируем шейдер
 GL.CompileShader(vShader);

 ShaderLog("vertex shader", vShader);

 //! Создаем фрагментный шейдер
 fShader = GL.CreateShader(ShaderType.FragmentShader);
 //! Передаем исходный код
 GL.ShaderSource(fShader, fsSource);
 //! Компилируем шейдер
 GL.CompileShader(fShader);

 ShaderLog("fragment shader", fShader);

 //! Создаем программу и прикрепляем шейдеры к ней
 Program = GL.CreateProgram();
 GL.AttachShader(Program, vShader);
 GL.AttachShader(Program, fShader);

 //! Линкуем шейдерную программу
 GL.LinkProgram(Program);

 //Единственное, что не перевел на OpenTK:
 //! Проверяем статус сборки 
 //int link_ok;
 //GL.GetProgramiv(Program, GL_LINK_STATUS, &link_ok);
 //if(!link_ok)
 //{
 // std::cout << "error attach shaders \n";
 // return;
 //}

 //! Вытягиваем ID атрибута из собранной программы 
 const string attr_name = "coord";
 Attrib_vertex = GL.GetAttribLocation(Program, attr_name);
 if (Attrib_vertex == -1)
 {
 Console.WriteLine("could not bind attrib {0}", attr_name);
 return;
 }
 //! Вытягиваем ID юниформ
 const string unif_name = "color";
 Unif_color = GL.GetUniformLocation(Program, unif_name);
 if (Unif_color == -1)
 {
 Console.WriteLine("could not bind uniform {0}", unif_name);
 return;
 }

 CheckOpenGLerror();
 }

 //! Инициализация VBO
 void InitVBO()
 {
 GL.GenBuffers(1, out VBO);
 GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
 //! Вершины нашего треугольника
 Vertex[] triangle =
 {
 new Vertex {x = -1.0f, y = -1.0f},
 new Vertex {x = 0.0f, y = 1.0f},
 new Vertex {x = 1.0f, y = -1.0f}
 };
 //! Передаем вершины в буфер, 8 - количество байт в структуре 4 байта на 1 float, их у нас 2
 GL.BufferData(BufferTarget.ArrayBuffer, triangle.Length * 8, triangle, BufferUsageHint.StaticDraw);

 CheckOpenGLerror();
 }

 //! Освобождение шейдеров
 void FreeShader()
 {
 //! Передавая ноль, мы отключаем шейдрную программу
 GL.UseProgram(0);
 //! Удаляем шейдерную программу
 GL.DeleteProgram(Program);
 }

 //! Освобождение шейдеров
 void FreeVBO()
 {
 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
 GL.DeleteBuffers(1, ref VBO);
 }

 protected override void OnRenderFrame(FrameEventArgs e)
 {
 base.OnRenderFrame(e);

 GL.Clear(ClearBufferMask.ColorBufferBit);
 //! Устанавливаем шейдерную программу текущей
 GL.UseProgram(Program);

 //! Передаем юниформ в шейдер
 GL.Uniform4(Unif_color, 1.0f, 0.0f, 0.0f, 1.0f);

 //! Включаем массив атрибутов
 GL.EnableVertexAttribArray(Attrib_vertex);
 //! Подключаем VBO
 GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
 //! Указывая pointer 0 при подключенном буфере, мы указываем что данные в VBO
 GL.VertexAttribPointer(Attrib_vertex, 2, VertexAttribPointerType.Float, false, 0, 0);
 //! Отключаем VBO
 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
 //! Передаем данные на видеокарту(рисуем)
 GL.DrawArrays(PrimitiveType.Triangles, 0, 3); // 3 вершины

 //! Отключаем массив атрибутов
 GL.DisableVertexAttribArray(Attrib_vertex);

 //! Отключаем шейдерную программу
 GL.UseProgram(0);

 SwapBuffers();
 }

 protected override void OnResize(EventArgs e)
 {
 base.OnResize(e);

 GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
 }
 }

 class Program
 {
 static void Main(string[] args)
 {
 var g = new Game(); //Создание окна
 g.Run(30.0f);
 }
 }
}

 

Также нужно добавить System.Drawing:
SimpleShaderOpenTk1_5.PNG



Код хорошо описан в оригинальной статье, тут он делает тоже самое, структуру кода старался оставить такой же, так что проблем с пониманием быть не должно.

Результат работы программы:
SimpleShaderOpenTk1_6.PNG


Надеюсь заметка окажется полезной. Ошибки орфографии и оформления прошу слать в личку.
Всем привет.

После установки на windows 10 visual studio 2015, в списке элементов панели управления нет элемента simpleOpenGLControl. Но элемента не будет в списке .NET Framework Components.

9f04ec18a60976985d52e9b22f1e91e5.png

Проблема легко решается: в окне Choose Toolbox Items (Окно "выбор элементов панели управления" из урока по ссылке выше) на вкладке .NET Framework Components выбираем  Browse и переходим в  C:\Program Files (x86)\TaoFramework\bin. Выбираем Tao.Platform.Windows.dll

8b78149d4d085be172724861c41ea713.png

Затем фильтруем по слову simple (как на рисунке ниже) и выбираем нашедшийся компонент.

99a1b8a26473f67d4a18f18a7296a402.png

Доброго времени суток.

Это четвертая часть цикла статей "Разработка OpenGL приложения под Android".

Предыдущие статьи:
Разработка OpenGL приложения под Android. Часть 0. Введение.
Разработка OpenGL приложения под Android. Часть 1. Знакомство.
Разработка OpenGL приложения под Android. Часть 2. Матрицы.

Сегодня мы будем работать с освещением.
Это очень важная тема, умение работать с основами освещения пригодится Вам в большинстве Ваших собственных проектов.

Здесь может возникнуть вопрос зачем что-то менять, если по умолчанию в OpenGL уже выставлен источник света и мы и без того видим наши объекты.

Дело в том, что выставленный по умолчанию свет нельзя редактировать: изменять его цвет, насыщенность, положение и другие параметры. Но самое главное это то, что он всего один. Если Вам, к примеру, понадобится еще свет от ламп или фонарика, то стандартное освещение уже не поможет.




Доброго времени суток.

Это третья часть цикла статей "Разработка OpenGL приложения под Android".

Предыдущие статьи:
Разработка OpenGL приложения под Android. Часть 0. Введение.
Разработка OpenGL приложения под Android. Часть 1. Знакомство.

В прошлой статье мы создали, наверное, самое простое приложение, использующее мощь OpenGL, которое только может быть.
Это всего лишь микроскопическая часть возможностей этой библиотеки. В этой главе я хочу поговорить с Вами о матрицах.

Как пишут во всех уроках и статьях на данную тему, скажу Вам, что, действительно, необходимости знать все свойства матриц у Вас нет, но их понимание способствовало бы скорейшему усвоению всех процессов OpenGL. С помощью матриц происходят смещение, поворот и прочие искажения пространства.

В OpenGL 1.0(именно эту версию мы пока используем) есть три типа матриц: GL_MODELVIEW, GL_PROJECTION и GL_TEXTURE.
Остановимся на каждой из них немного подробнее.


c8b5ef0c246d3709bb9286d0878a8121.png


Доброго времени суток.

Это вторая часть цикла статей "Разработка OpenGL приложения под Android".

Первую часть можно найти здесь: Разработка OpenGL приложения под Android. Часть 0. Введение.

Начнем с того, что сегодня самый лучший день для того, чтобы написать свое первое приложение для android. Если у Вас что-то не получалось до этого момента - будьте уверены, сегодня все получится!

И так. Вы установили новейшую версию Android Studio и все необходимые компоненты для работы с Android 4.4? Немного пощупали, изучили основные принципы работы в данной IDE и готовы покорять мир 3D графики? Отлично! Тогда приступим к работе.
  • В первую очередь создадим проект и назовем его OGLPart1.
Вы можете назвать его как угодно, но мне так будет удобнее. Уроков будет много и что бы не запутаться в них я буду нумеровать проекты эквивалентно статьям. Здесь все просто. Все, что от Вас требуется это указать имя проекта, папку для его хранения и минимальную поддерживаемую версию android. Стандартная процедура.
13044
24.10.2015
18
0 
Смотреть публикацию


eb8764cc18c904f961e8b2c0d5ca9a0e.png

Доброго времени суток.

Это первая статья из серии программирования 3D графики с использованием OpenGL в среде android приложений.

Я хотел бы начать с полезных ссылок, которые не раз пригодятся Вам при изучении данной темы.

Это Ваш главный ресурс и помощник. Именно здесь лучше всего получать информацию о принципах работы команд и пояснения по функциям и их аргументам. Вас немного может приостановить тот факт, что сайт практически весь на английском языке, но это можно превратить в плюс. Если взять себя в руки и начать переводить неясные Вам вещи, то материал будет усвоен в разы лучше, ведь Вы хорошо поработаете для его изучения. Возможно, получится подучить язык, а это никогда не будет лишним.

В общем и целом, я хотел бы привить Вам большую любовь к изучению документаций. Не важно чем Вы решите заняться в будущем - официальная документация, практически в любом случае, будет полезнее тех статей, что Вы найдете в сети.
Доброго времени суток!
Мое имя в сети Freaky Brainstorm и я android разработчик в одной из частных компаний. Начинающий, совсем еще «зеленый» разработчик, но все когда-то с этого начинали.
Своими публикациями я хотел бы упростить жизнь некоторым людям, которые в связи со своей неопытностью, как и я столкнутся с проблемами, с которыми столкнулся я на пути android разработки.
Приложение над которым я работаю в данный момент очень связанно с 3D графикой и, скорее всего, основной упор в блоге будет сделан на OpenGL.
Я ни в коем случае, не утверждаю, что мои решения являются верными, и что поступать нужно именно таким образом который предлагаю я. Вовсе нет, здесь я просто описываю свое видение решения стоящих передо мной проблем программирования.
Если вы в первый раз открыли проект, скачав исходники с сайта и при попытки компиляции и запуска у вас вылетело исключение (щелкните на картинках для увеличения)


4be92c9009a758be9f59b98722830f45.png


BadImageFormatException was unhandled
Была сделана попытка загрузить программу, имеющую неверный формат. (Исключение из HRESULT: 0x8007000B)

на строке Glut.glutInit(); (см рисунок выше), то вам необходимо в  в поле Platform установить x86 вместо значения Any CPU:



2ac2670e3a0f4bf85c93874f29dc18c3.png


bd37e492fd4060c3fa939ec622a1a510.png



Затем снова откомпилируйте проект. Удачи! 8)
Часто приходится вести отладочную информацию, чаще всего, в простых случаях это делается в консоль. Но гораздо чаще необходимо вести лог и в консоль, и в файл.
Поведаю Вам о простом классике логирования, который симулирует стандартные cout, cerr, используя шаблонную функцию-оператор <<. Расскажу о некоторых специфичных моментах с которыми столкнулся.

Как известно Tao Framework, уже не развивается, и даже на его странице sourceforge, пишут: "возьмите OpenTK, как замену".
Но вот появился луч надежды на возрождение, Tao Framework.
В этом части я расскажу вам, какие бывают виды организации он-лайн игр, в чем их плюсы и минусы. Итак, начнем.
Всем привет. С момента, когда я написал свою последнюю статью прошло ровно девять месяцев. За это время я успел поступить в универ и узнать много нового в области программирования, с чем спешу поделиться. Хочу запустить цикл статей о том, как написать он-лайн игру средствами .NET. Подробно расскажу о клиент-серверной архитектуры приложения, о организации двухстороннего клиента без использования сервера, и многое многое другое.

Я сразу хотел бы поинтересоваться у вас, чего вы ждете от прочтения всех статей в цикле: чему бы хотели научиться, и о чем рассказать подробней. Свои мысли излагайте в комментариях к топику.

Цикл обещает быть объемным а над первой статьей я уже работаю в свободное от учебы время (таки сессия на носу). В конце, да и в процессе создания цикла будем писать две простеньких 2D игрушки, которые будут на своем примере показывать, как можно использовать протокол TCP\IP в разработке игр. Так что ждите=)

Уже кое что готово:

Пишем он-лайн игру: Часть первая
Товарищи, делаю все как в уроке, но вылетает черный экран!!!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
// для работы с библиотекой OpenGL
using Tao.OpenGl;
// для работы с библиотекой FreeGLUT
using Tao.FreeGlut;
// для работы с элементом управления SimpleOpenGLControl
using Tao.Platform.Windows;

namespace laba1
{
public partial class Form1: Form
{
public Form1()
{
InitializeComponent();
AnT.InitializeContexts();
}

private void Form1_Load(object sender, EventArgs e)
{

// инициализация Glut
Glut.glutInit();
Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE | Glut.GLUT_DEPTH);

// очитка окна
Gl.glClearColor(255, 255, 255, 0);//выставление цвета основного

// установка порта вывода в соотвествии с размерами элемента anT
Gl.glViewport(0, 0, AnT.Width, AnT.Height);

// настройка проекции
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluOrtho2D(0, 56 * AnT.Width / AnT.Height, 0, 56);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
Gl.glLoadIdentity();
???
Вы еще не используете VA? Тогда мы идем к вам!

Выходят новые версии OpenGL. И все время, появляется информация, о том, что какие-то функции уже не рекомендуемые,а какие-то удалены. Фиксированный графический конвейер(ФГК) уходит, с новыми версиями OpenGL. Точнее он уже ушел, в OpenGL 3.3, core profile его уже нет. А, что же тогда приходит на смену традиционному, старому функционалу?!
Всем привет, имеется проблема, при наложении текстуры и изменения её размера она ложится плиткой, пытаюсь поставить TEXTURE_NO_TILE в настройках текстуры, однако это не к чему не приводит.
Вот структура класса текстуры и меша
 [Flags]
    public enum Lib3dsTextureMapFlags
    {
        LIB3DS_TEXTURE_DECALE = 0x0001,
        LIB3DS_TEXTURE_MIRROR = 0x0002,
        LIB3DS_TEXTURE_NEGATE = 0x0008,
        LIB3DS_TEXTURE_NO_TILE = 0x0010,
        LIB3DS_TEXTURE_SUMMED_AREA = 0x0020,
        LIB3DS_TEXTURE_ALPHA_SOURCE = 0x0040,
        LIB3DS_TEXTURE_TINT = 0x0080,
        LIB3DS_TEXTURE_IGNORE_ALPHA = 0x0100,
        LIB3DS_TEXTURE_RGB_TINT = 0x0200
    }
 public class Lib3dsTextureMap
    {
        //public uint user_id;
        //public object user_ptr;
        public string name = "";
        public Lib3dsTextureMapFlags flags;
        public float percent;
        public float blur;
        public float[] scale = new float[2];
        public float[] offset = new float[2];
        public float rotation;
        public float[] tint_1 = new float[3];
        public float[] tint_2 = new float[3];
        public float[] tint_r = new float[3];
        public float[] tint_g = new float[3];
        public float[] tint_b = new float[3];
    }
public class Lib3dsMaterial
    {
        //public uint user_id;
        //public object user_ptr;
        public string name = "";                                  // Material name
        public float[] ambient = new float[3];    // Material ambient reflectivity
        public float[] diffuse = new float[3];    // Material diffuse reflectivity
        public float[] specular = new float[3];   // Material specular reflectivity
        public float shininess;                                 // Material specular exponent
        public float shin_strength;
        public bool use_blur;
        public float blur;
        public float transparency;
        public float falloff;
        public bool is_additive;
        public bool self_illum_flag;
        public float self_illum;
        public bool use_falloff;
        public int shading;
        public bool soften;
        public bool face_map;
        public bool two_sided;                  // Material visible from back
        public bool map_decal;
        public bool use_wire;
        public bool use_wire_abs;
        public float wire_size;
        public Lib3dsTextureMap texture1_map = new Lib3dsTextureMap();
        public Lib3dsTextureMap texture1_mask = new Lib3dsTextureMap();
        public Lib3dsTextureMap texture2_map = new Lib3dsTextureMap();
        public Lib3dsTextureMap texture2_mask = new Lib3dsTextureMap();
        public Lib3dsTextureMap opacity_map = new Lib3dsTextureMap();
        public Lib3dsTextureMap opacity_mask = new Lib3dsTextureMap();
        public Lib3dsTextureMap bump_map = new Lib3dsTextureMap();
        public Lib3dsTextureMap bump_mask = new Lib3dsTextureMap();
        public Lib3dsTextureMap specular_map = new Lib3dsTextureMap();
        public Lib3dsTextureMap specular_mask = new Lib3dsTextureMap();
        public Lib3dsTextureMap shininess_map = new Lib3dsTextureMap();
        public Lib3dsTextureMap shininess_mask = new Lib3dsTextureMap();
        public Lib3dsTextureMap self_illum_map = new Lib3dsTextureMap();
        public Lib3dsTextureMap self_illum_mask = new Lib3dsTextureMap();
        public Lib3dsTextureMap reflection_map = new Lib3dsTextureMap();
        public Lib3dsTextureMap reflection_mask = new Lib3dsTextureMap();
        public uint autorefl_map_flags;
        public int autorefl_map_anti_alias; // 0=None, 1=Low, 2=Medium, 3=High
        public int autorefl_map_size;
        public int autorefl_map_frame_step;
    }
 public class Lib3dsMesh
    {
        //public uint user_id;
        //public object user_ptr;
        public string name = "";                                  // Mesh name. Don't use more than 8 characters
        public Lib3dsObjectFlags object_flags;  // see Lib3dsObjectFlags
        public byte color;                                              // Index to editor palette [0..255]
        public float[,] matrix = new float[4, 4]; // Transformation matrix for mesh data
        public ushort nvertices;                                // Number of vertices in vertex array (max. 65535)
        public List<Lib3dsVertex> vertices;
        public List<Lib3dsTexturecoordinate> texcos;
        public List<ushort> vflags;
        public ushort nfaces;                                   // Number of faces in face array (max. 65535)
        public List<Lib3dsFace> faces;
        public string box_front = "";
        public string box_back = "";
        public string box_left = "";
        public string box_right = "";
        public string box_top = "";
        public string box_bottom = "";
        public Lib3dsMapType map_type;
        public float[] map_pos = new float[3];
        public float[,] map_matrix = new float[4, 4];
        public float map_scale;
        public float[] map_tile = new float[2];
        public float[] map_planar_size = new float[2];
        public float map_cylinder_height;
    }
1820
13.08.2012
14
0 
Смотреть публикацию
Отрисовка текста в OpenGL, обычно нетривиальная задача, особенно для новичков. Стандартно эта задача сводится к рисованию прямоугольников с натянутой текстурой, на которой отображена та или иная буква. Располагая, которые, относительно друг, друга собираются в слова.
Когда-то, когда я еще учился в Академии, мы делали на ASP.NET капчу отрисовывая строки текста и линии в картинку. На C# это делается очень легко, встроенными методами класса Graphics. Им и воспользуемся для рисования нашего текста.
В этом посте я покажу очень примитивный и к сожалению не масштабируемый пример.=)
Всем привет, есть такая проблема, нужно загруженную часть 3Д модели отрисовать в JPG файле. В принципи частично у меня это получилось, однако отрисовывается только часть модели (скорее всего она не под нужным углом повёрнута, как развернуть её я хз). Или я гдето как обычно натупил. Подскажите где ошибка, или как это можно иначе реализовать?
2241
03.08.2012
13
0 
Смотреть публикацию
Думаю практически все программисты(и не только) знают или хотя бы слышали, что такое freetype. Поэтому не будет долгих вступлении, копипаста информации с других ресурсов, о том, что такое freetype.
Turbulenz WebGL Engine. Quake 4 в браузере

Видео

Движок Turbulenz

Turbulenz - игровой движок для социальных игр, использующий HTML 5, WebGL JavaScript. Его возможности позволяют делать захватающие реалтайм 3D игры, с физикой.

Всем доброго времени суток, подскажите как отобразить текстуры  3d объекта, в окне SimpleOpenglControl
2252
26.07.2012
26
0 
Смотреть публикацию
^
Регистрация
Регистрируясь, вы принимаете правила сайта. Если вы не получили код подтв. регистрации - не забудьте проверить папку спам.
Логин*
Email*
Пароль*
Подтверждение пароля*
 
Логин*
Код*
 
×
Восстановление пароля
Пожалуйста, заполните поля, после чего вы получите код подтверждения на ваш Email. Если код не пришел в течении нескольких минут - проверьте папку спам.
Логин

или Email
Логин*
Код подтверждения*
Новый пароль*
Подтверждение пароля*
×
Авторизация
  • Используйте вашу учетную запись на Facebook.com для входа на сайт.
  • Используйте вашу учетную запись VKontakte для входа на сайт.
  • Используйте вашу учетную запись Google для входа на сайт.
Авторизуйтесь с помощью соц. сети или с помощью аккаунта на сайте:
×