OpenGL шейдеры. Простой шейдер на GLSL в C# OpenTK
Когда-то я ознакомился с уроком "Простой шейдер на GLSL", и это вдохновило на изучение шейдеров в OpenGL и сам современный OpenGL в частности. В основном я работаю с C# поэтому после ознакомления с кодом на C++ и его сборкой, приступил к переводу его в С#. Эта заметка как раз о том, как перенести код из урока на C#. Для доступа к OpenGL будет использоваться современная версия враппера OpenTK.
И так сперва создадим консольный проект на C#:
Подключим OpenTk через nuget
если не нашло OpenTk то нужно включить репоизоторий в настройках:
Используем следующий код:
/*http://esate.ru, badcat*/
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:
Код хорошо описан в оригинальной статье, тут он делает тоже самое, структуру кода старался оставить такой же, так что проблем с пониманием быть не должно.
Результат работы программы:
Надеюсь заметка окажется полезной. Ошибки орфографии и оформления прошу слать в личку.
Понравилась публикация? Сохраните ее, чтобы вернуться к изучению материала!