OpenGL шейдеры: Вторая часть. GLSL Шейдер в OpenTK
В продолжение перевода с С++ на C#/OpenTk, привожу вторую и последнюю часть цикла уроков. В прошлой заметке всё было усыпано скриншотами, о том как создать и настроить проект, а далее приводился лишь кусок кода. В этот раз будет по большей части код. Основной момент, состоит в том, что вместо GLM из плюсового кода мы используем возможности OpenTk для работы с матрицами.
Код:
/*http://esate.ru, badcat*/
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.
Результат работы:
Впрочем он должен быть такой же, как в оригинальном плюсовом коде.
Стоит ли переходить на Windows 10?Windows 10 установлена на каждый 3-й компьютер. Какие плюсы от перехода? DirectX 12 работает только в Windows 10?
Как установить Windows 10?Как бесплатно и легально скачать? Как записать и установить с последними обновлениями?
Сохраните страницу!
Регистрация
Регистрируясь, вы принимаете правила сайта. Если вы не получили код подтв. регистрации - не
забудьте проверить папку спам.
×
Восстановление пароля
Пожалуйста, заполните поля, после чего вы получите код подтверждения на ваш Email. Если код не пришел в течении нескольких минут - проверьте папку спам.
×
Авторизация
Авторизуйтесь с помощью соц. сети или с помощью аккаунта на сайте: