Шейдер вершин получает из программы число секунд, прошедших с момента старта часов watch, и формирует цвет и позицию вершины (частицы):
// Particles vertex shader (файл particlesVS.glsl)
varying vec4 color;
uniform float time;
void main()
{
float vel = 0.05; // Скорость частицы
vec3 pos = gl_Vertex.xyz * vel * time;
color = vec4(max(abs(pos.x), 0.1), max(abs(pos.y), 0.1), max(abs(pos.z), 0.1), 1.0);
gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 1.0);
}
Частицы перемещаются дискретно, меняя позициию каждую секунду. Часы watch обнуляются каждые 60 с.
Шейдер фрагментов получает от шейдера вершин цвет и закрашивает им частицу.
// Particles fragment shader (файл particlesFS.glsl)
varying vec4 color;
void main()
{
gl_FragColor = color;
}
На рис. 1 показаны частицы через 36 с после запуска программы.
Рис. 1. Частицы через 36 с после запуска программы
using System;
using System.IO;
using System.Diagnostics; // Stopwatch
using OpenTK;
using OpenTK.Graphics.OpenGL;
namespace ParticlesShader
{
public class Particles_shader: GameWindow
{
public Particles_shader() : base(400, 300) { }
// Шейдеры вершин и фрагментов
int VertexShaderObject, FragmentShaderObject, ProgramObject;
const string VertexShaderFilename = "particlesVS.glsl";
const string FragmentShaderFilename = "particlesFS.glsl";
const int numParticles = 500;
Vector4d[] pAttr = new Vector4d [numParticles];
float time;
Stopwatch watch = new Stopwatch();
Random rnd = new Random();
private int readShader(string shaderFilename, ShaderType st, string vs)
{
string LogInfo;
int shaderObject = GL.CreateShader(st);
using (StreamReader sr = new StreamReader(shaderFilename))
{
GL.ShaderSource(shaderObject, sr.ReadToEnd());
GL.CompileShader(shaderObject);
return shaderObject;
}
GL.GetShaderInfoLog(shaderObject, out LogInfo);
if (LogInfo.Length > 0 & !LogInfo.Contains("hardware"))
Console.WriteLine("Ошибка компиляции шейдера " + vs + "\nLog:\n" + LogInfo);
else
Console.WriteLine("Компиляция шейдера " + vs + " завершена успешно");
}
protected override void OnLoad(EventArgs e)
{
string LogInfo;
int[] temp = new int[1];
// Загрузка и компиляция шейдеров вершин и фрагментов
VertexShaderObject = readShader(VertexShaderFilename, ShaderType.VertexShader, "вершин");
FragmentShaderObject = readShader(FragmentShaderFilename, ShaderType.FragmentShader, "фрагментов");
// Связываем шейдеры с рабочей программой
ProgramObject = GL.CreateProgram();
GL.AttachShader(ProgramObject, VertexShaderObject);
GL.AttachShader(ProgramObject, FragmentShaderObject);
// Связываем все вместе
GL.LinkProgram(ProgramObject);
// Удаляем ранее созданные шейдеры
GL.DeleteShader(VertexShaderObject);
GL.DeleteShader(FragmentShaderObject);
GL.GetProgram(ProgramObject, GetProgramParameterName.LinkStatus, out temp[0]);
Console.WriteLine("Связывание программ (" + ProgramObject + ") " + ((temp[0] == 1) ? "выполнено." : "НЕ ВЫПОЛНЕНО"));
if (temp[0] != 1) // В случае неудачи при связывании
{
GL.GetProgramInfoLog(ProgramObject, out LogInfo);
Console.WriteLine("Информация:\n" + LogInfo);
}
GL.GetProgram(ProgramObject, GetProgramParameterName.ActiveAttributes, out temp[0]);
Console.WriteLine("Зарегестрировано " + temp[0] + " атрибута");
Console.WriteLine("Создание шейдера завершено. GL-ошибка: " + GL.GetError());
Console.WriteLine("");
// Генерация массива частиц
for (int i = 0; i < numParticles; i++)
pAttr[i] = new Vector4d(0.5 - rnd.NextDouble(), 0.5 - rnd.NextDouble(), 0.5 - rnd.NextDouble(), 0.5 - rnd.NextDouble());
watch.Start();
GL.ClearColor(0f, 0f, 0f, 1f); // Цвет фона
GL.PointSize(6f); // Размер частицы
GL.Enable(EnableCap.PointSmooth);
}
protected override void OnUnload(EventArgs e)
{
GL.DeleteProgram(ProgramObject);
base.OnUnload(e);
}
// Обработчик изменения размеров окна графического вывода
protected override void OnResize(EventArgs e)
{
GL.Viewport(0, 0, Width, Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
base.OnResize(e);
}
// Обработчик обновления
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
}
// Обработчик воспроизведения
protected override void OnRenderFrame(FrameEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit);
time = watch.Elapsed.Seconds;
this.Title = "Particles shader. Time " + time;
GL.UseProgram(ProgramObject);
GL.Uniform1(GL.GetUniformLocation(ProgramObject, "time"), time);
GL.Begin(PrimitiveType.Points);
for(int i = 0; i < numParticles; i++) GL.Vertex4(pAttr[i]);
GL.End();
GL.UseProgram(0);
this.SwapBuffers();
}
// Точка входа
[STAThread]
public static void Main()
{
using (Particles_shader ps = new Particles_shader())
{
ps.Title = "Particles shader";
ps.Run(10.0, 2.0);
}
}
}
}