Примеры на шейдерном OpenGL и WebGL

Я использую C#/OpenTK и TypeScript/WebGL. Вы можете выкладывать примеры на любом языке. Главное, чтобы это был шейдерный OpenGL.

OpenTK ставится из NuGet и позволяет создавать окно из консольного проекта. Проект с OpenTK откроется в VS для Windows, в VS для Mac и в MonoDevelop для Linux. А так же можно поставить из NuGet контрол OpenTK.GLControl, который позволит сочетать стандартные GUI-элементы WinForms и WPF с OpenGL графикой. Порт уроков из learnopengl.com на OpenTK на официальном сайте: https://opentk.net/learn/index.html

Содержание

Загрузка BMFont для вывода текста на OpenGL 2.0, C# и OpenTK

Вывод теста - это наиболее важный этап. Текст нужен при построении графиков, в 3D приложениях с интерактивной графикой, например, для вывода размеров деталей, в игрушках для вывода очков, жизней и т.д.

Я использовал в данном примере объединение методов из данных книг:

Исходники: https://github.com/8Observer8/BMFont_OpenTkOpenGL20CSharp

BMFont_OpenTkOpenGL20CSharp

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

1 Симпатия

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

@BlackFregat я собираюсь купить MacBook через один или через два года. Тогда попробую найти проблему, а сейчас я меня это большая загадка.

@8Observer8, неожиданно пошло, видимо, что-то у меня заклинило.

Но криво:
Снимок экрана 2019-12-15 в 18.18.20

1 Симпатия

На Windows проблем нет.

1 Симпатия

Примеры на JSFiddle на JavaScript/ES5 из книги "WebGL. Программирование трехмерной графики"

Я покупал перевод этой книги в бумажном виде на Ozon в 2016 году. Я прочитал несколько раз перевод - он очень хороший. На данный момент я изучаю оригинал в электронном виде и перевожу примеры на TypeScript и glMatrix (glMatrix - библиотека для работы с линейной алгеброй). Для TypeScript нужно дополнительно к модулю “gl-matrix” поставить модуль: npm i -D @types/gl-matrix В книге используется своя библиотека для работы с линейной алгеброй, написанная авторами книги: Коичи Мацуда и Роджер Ли

Все примеры на JavaScript/ES5 в оригинале, как они есть в книге. Вы можете запустить их в один клик в браузере на любой платформе и ОС без скачиваний и установки чего-либо. Делайте Fork, экспериментируйте - это лучший способ чему-то научиться. Переводите эти примеры на OpenGL 3+ и любые языки программирования: C#, Java, Python, C++ и т.д. Примеры на WebGL и WebGL выглядят практически один в один на любых языках программирования и переписываются с языка на язык очень легко и быстро.

Chapter 02. Your First Step with WebGL
ch02/HelloCanvas: https://jsfiddle.net/8Observer8/2gky294r/
ch02/HelloPoint1: https://jsfiddle.net/8Observer8/cc72u1u5/
ch02/HelloPoint2: https://jsfiddle.net/8Observer8/uxw657ud/
ch02/ClickedPoints: https://jsfiddle.net/8Observer8/xf4fnc0o/
ch02/ColoredPoints: https://jsfiddle.net/8Observer8/gkkmnpga/

Chapter 03. Drawing and Transforming Triangles
ch03/MultiPoint: https://jsfiddle.net/8Observer8/cty1120m/
ch03/HelloTriangle: https://jsfiddle.net/8Observer8/wk4sksnw/
ch03/HelloQuad: https://jsfiddle.net/8Observer8/g4ctyk7w/
ch03/HelloQuad_FAN: https://jsfiddle.net/8Observer8/v119e8o6/
ch03/HelloTriangle_LINES: https://jsfiddle.net/8Observer8/wwrkaxcf/
ch03/HelloTriangle_LINE_STRIP: https://jsfiddle.net/8Observer8/3ggjz4rm/
ch03/HelloTriangle_LINE_LOOP: https://jsfiddle.net/8Observer8/7vcyquro/
ch03/TranslatedTriangle: https://jsfiddle.net/8Observer8/0dp4xvyt/
ch03/RotatedTriangle: https://jsfiddle.net/8Observer8/gh9s6szm/
ch03/RotatedTriangle_Matrix: https://jsfiddle.net/8Observer8/7ze7pgpu/
ch03/ScaledTriangle_Matrix: https://jsfiddle.net/8Observer8/6xzoe63s/

Chapter 04. More Transformations and Basic Animation
ch04/RotatedTriangle_Matrix4: https://jsfiddle.net/8Observer8/t4y7783v/
ch04/RotatedTranslatedTriangle: https://jsfiddle.net/8Observer8/b5yfxojp/
ch04/TranslatedRotatedTriangle: https://jsfiddle.net/8Observer8/o8voebc9/
ch04/RotatingTriangle: https://jsfiddle.net/8Observer8/x9j5vdk7/
ch04/RotatingTranslatedTriangle: https://jsfiddle.net/8Observer8/rkrv0322/
ch04/RotatingTriangle_withButtons: https://jsfiddle.net/8Observer8/wzoLmdzd/

Chapter 05. Using Colors and Texture Images
ch05/MultiAttributeSize: https://jsfiddle.net/8Observer8/dsfgezbj/
ch05/MultiAttributeSize_Interleaved: https://jsfiddle.net/8Observer8/bshwnden/
ch05/MultiAttributeColor: https://jsfiddle.net/8Observer8/bveykLdf/
ch05/ColoredTriangle: https://jsfiddle.net/8Observer8/mrkpms7d/
ch05/HelloTriangle_FragCoord: https://jsfiddle.net/8Observer8/ft33yo9s/
ch05/TexturedQuad: https://jsfiddle.net/8Observer8/o3vakb3h/
ch05/TexturedQuad_Repeat: https://jsfiddle.net/8Observer8/2s7q68cc/
ch05/TexturedQuad_Clamp_Mirror: https://jsfiddle.net/8Observer8/mqu0wwma/
ch05/MultiTexture: https://jsfiddle.net/8Observer8/ztew5u0p/

Chapter 07. Toward the 3D World
ch07/LookAtTriangles: https://jsfiddle.net/8Observer8/6ab11xpg/
ch07/LookAtRotatedTriangles: https://jsfiddle.net/8Observer8/944dd57h/
ch07/LookAtRotatedTriangles_modelViewMatrix: https://jsfiddle.net/8Observer8/e5t6gj1w/
ch07/LookAtTrianglesWithKeys: https://jsfiddle.net/8Observer8/38ewegg2/
ch07/OrthoView: https://jsfiddle.net/8Observer8/zebt4u7t/
ch07/LookAtTrianglesWithKey_ViewVolume: https://jsfiddle.net/8Observer8/vLcejtm1/
ch07/OrthoView_halfSize: https://jsfiddle.net/8Observer8/uvcd9h4p/
ch07/OrthoView_halfWidth: https://jsfiddle.net/8Observer8/vepodfb8/
ch07/PerspectiveView: https://jsfiddle.net/8Observer8/640pv8qe/
ch07/PerspectiveView_mvp: https://jsfiddle.net/8Observer8/w8yh4Lmj/
ch07/PerspectiveView_mvpMatrix: https://jsfiddle.net/8Observer8/hhwnx145/
ch07/DepthBuffer: https://jsfiddle.net/8Observer8/hyumw026/
ch07/Zfighting: https://jsfiddle.net/8Observer8/foc0b45t/
ch07/HelloCube: https://jsfiddle.net/8Observer8/rkpn5tyw/
ch07/ColoredCube: https://jsfiddle.net/8Observer8/80x8cyom/
ch07/ColoredCube_singleColor: https://jsfiddle.net/8Observer8/pespackq/

Chapter 08. Lighting Objects
ch08/LightedCube: https://jsfiddle.net/8Observer8/4jchxo84/
ch08/LightedCube_animation: https://jsfiddle.net/8Observer8/ekw3osj7/
ch08/LightedCube_ambient: https://jsfiddle.net/8Observer8/y6qwnfe1/
ch08/LightedTranslatedRotatedCube: https://jsfiddle.net/8Observer8/pa88ujjg/
ch08/PointLightedCube: https://jsfiddle.net/8Observer8/vuq118ue/
ch08/PointLightedCube_animation: https://jsfiddle.net/8Observer8/5bj39hb8/
ch08/PointLightedSphere: https://jsfiddle.net/8Observer8/edz9Lz8f/
ch08/PointLightedSphere_perFragment: https://jsfiddle.net/8Observer8/qzwyow4j/
ch08/PointLightedCube_perFragment: https://jsfiddle.net/8Observer8/8t1umamf/
ch08/LightedCube_perFragment: https://jsfiddle.net/8Observer8/471y2t84/

Chapter 09. Hierarchical Objects
ch09/JointModel: https://jsfiddle.net/8Observer8/vqse5egz/
ch09/MultiJointModel: https://jsfiddle.net/8Observer8/sL53wkn3/
ch09/MultiJointModel_segment: https://jsfiddle.net/8Observer8/ygvk7odv/

Chapter 10. Advanced Techniques
ch10/RotateObject: https://jsfiddle.net/8Observer8/1f5hLmff/
ch10/PickObject: https://jsfiddle.net/8Observer8/owue624n/
ch10/PickFace: https://jsfiddle.net/8Observer8/edvw6z90/
ch10/HUD: https://jsfiddle.net/8Observer8/fLxxxs35/
ch10/3DoverWeb: https://jsfiddle.net/8Observer8/tbowcc16/
ch10/Fog: https://jsfiddle.net/8Observer8/6yf9L399/
ch10/Fog_w: https://jsfiddle.net/8Observer8/8aLvthc3/
ch10/RoundedPoints: https://jsfiddle.net/8Observer8/sjs5kmn4/
ch10/LookAtBlendedTriangles: https://jsfiddle.net/8Observer8/apoz294n/
ch10/BlendedCube: https://jsfiddle.net/8Observer8/xsrL2fs5/
ch10/ProgramObject: https://jsfiddle.net/8Observer8/jnd0j6w0/
ch10/FramebufferObject: https://jsfiddle.net/8Observer8/vaLq6d66/
ch10/Shadow: https://jsfiddle.net/8Observer8/jsnfwcae/
ch10/Shadow_highp: https://jsfiddle.net/8Observer8/brjzr00n/
ch10/Shadow_highp_sphere: https://jsfiddle.net/8Observer8/4fmyLy5f/
ch10/OBJViewer: https://jsfiddle.net/8Observer8/pws1x7uv/
ch10/RotatingTriangle_contextLost: https://jsfiddle.net/8Observer8/vs01s8Lz/

Gifts
gifts/Particle: https://jsfiddle.net/8Observer8/Ltzt31vk/
gifts/Printf: https://jsfiddle.net/8Observer8/qsw7jtec/
gifts/SpecularCube: https://jsfiddle.net/8Observer8/z4xj9rbv/
gifts/TextTexture: https://jsfiddle.net/8Observer8/qt7q2kuf/
gifts/ThreeDUI: https://jsfiddle.net/8Observer8/zdw1f2st/
gifts/Wave: https://jsfiddle.net/8Observer8/eL9odthz/
gifts/WorldCoordinateSystem: https://jsfiddle.net/8Observer8/6utj3hnk/
appendix/CoordinateSystem: https://jsfiddle.net/8Observer8/dzz056jt/

Appendix
appendix/CoordinateSystem_viewVolume: https://jsfiddle.net/8Observer8/apxLww1q/
appendix/LoadShaderFromFiles: https://jsfiddle.net/8Observer8/wdn9ubhj/

В этой инструкции есть пример рисование графики на OpenGL на контроле OpenTK.GLControl в WPF приложении: Инструкция. Добавляем OpenGL графику на WPF форму с помощью OpenTK.GLControl

Таким образом можно объединять GUI на WPF и графику на OpenGL, то есть встраивать 2D или 3D графику внутрь WPF приложения.

Пример сравнения создания шейдерной программы на C# и TypeScript

Код практически идентичен:

C#

public class ShaderProgram
{
    private static string _vShaderSource =
        @"
            /* ... */
        ";

    private static string _fShaderSource =
        @" 
            /* ... */
        ";

    public static int Create()
    {
        int vShader = CreateShader(_vShaderSource, ShaderType.VertexShader);
        int fShader = CreateShader(_fShaderSource, ShaderType.FragmentShader);
        if (vShader == -1 || fShader == -1)
        {
            return -1;
        }

        int program = GL.CreateProgram();
        GL.AttachShader(program, vShader);
        GL.AttachShader(program, fShader);
        GL.LinkProgram(program);
        int ok;
        GL.GetProgram(program, GetProgramParameterName.LinkStatus, out ok);
        if (ok == 0)
        {
            Console.WriteLine("Failed to link a program. Error: " + GL.GetProgramInfoLog(program));
            return -1;
        }
        GL.UseProgram(program);

        return program;
    }

    /* ... */
}

TypeScript

export class ShaderProgram
{
    private static vShaderSource =
        `
            /* ... */
        `;

    private static fShaderSource =
        `
            /* ... */
        `;

    public static Create(): WebGLProgram
    {
        let vShader = this.CreateShader(this.vShaderSource, gl.VERTEX_SHADER);
        let fShader = this.CreateShader(this.fShaderSource, gl.FRAGMENT_SHADER);
        if (vShader === null || fShader === null)
        {
            return null;
        }

        let program = gl.createProgram();
        gl.attachShader(program, vShader);
        gl.attachShader(program, fShader);
        gl.linkProgram(program);
        let ok = gl.getProgramParameter(program, gl.LINK_STATUS) as boolean;
        if (!ok)
        {
            console.log("Failed to link a program. Error: " + gl.getProgramInfoLog(program));
            return null;
        }
        gl.useProgram(program);

        return program;
    }

    /* ... */
}

Рисование логотипа Mitsubishi из кода на OpenGL 3, GLSL и C#. Окно создаётся из консольного проекта с помощью библиотеки OpenTK, которая ставится через NuGet.

MitsubishiLogoFromDae_OpenTkOpenGL30CSharp

Program.cs

using System;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;
using OpenTK;
 
namespace MitsubishiSign
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var window = new Window())
            {
                window.Title = "Mitsubishi";
                window.Run(60);
            }
        }
    }
 
    class Window : GameWindow
    {
        private Matrix4 _projMatrix;
        private Matrix4 _modelMatrix;
        private Matrix4 _mpMatrix;
        private int _uMPMatrixLocation;
 
        public Window() : base(250, 250, new GraphicsMode(32, 0, 0, 8)) { }
 
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            var vShaderSource =
                @"
                    #version 130
 
                    in vec3 aPosition;
                    uniform mat4 uMPMatrix;
 
                    void main()
                    {
                        gl_Position = uMPMatrix * vec4(aPosition, 1.0);
                    }
                ";
            var fShaderSource =
                @"
                    #version 130
                    precision mediump float;
 
                    out vec4 fragColor;
 
                    void main()
                    {
                        fragColor = vec4(0.0);
                    }
                ";
            var vShader = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vShader, vShaderSource);
            GL.CompileShader(vShader);
            var fShader = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fShader, fShaderSource);
            GL.CompileShader(fShader);
            var program = GL.CreateProgram();
            GL.AttachShader(program, vShader);
            GL.AttachShader(program, fShader);
            GL.LinkProgram(program);
            GL.UseProgram(program);
 
            int vbo;
            GL.CreateBuffers(1, out vbo);
            float[] positions = new float[]
            {
                0f, -0.866f, -1f,
                0.5f, 0f, -1f,
                0f, 0.866f, -1f,
                -0.5f, 0f, -1f
            };
            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            GL.BufferData(BufferTarget.ArrayBuffer, sizeof(float) * positions.Length, positions, BufferUsageHint.StaticDraw);
            var aPositionLocation = GL.GetAttribLocation(program, "aPosition");
            GL.VertexAttribPointer(aPositionLocation, 3, VertexAttribPointerType.Float, false, 0, 0);
            GL.EnableVertexAttribArray(aPositionLocation);
 
            _uMPMatrixLocation = GL.GetUniformLocation(program, "uMPMatrix");
 
            GL.ClearColor(1f, 1f, 1f, 1f);
        }
 
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);
            GL.Clear(ClearBufferMask.ColorBufferBit);
 
            _modelMatrix =
                Matrix4.CreateScale(5f, 5f, 1f) *
                Matrix4.CreateTranslation(0f, 0.866f * 5f, 0f);
            _mpMatrix = _modelMatrix * _projMatrix;
            GL.UniformMatrix4(_uMPMatrixLocation, false, ref _mpMatrix);
            GL.DrawArrays(PrimitiveType.TriangleFan, 0, 4);
 
            _modelMatrix =
                Matrix4.CreateScale(5f, 5f, 1f) *
                Matrix4.CreateTranslation(0f, 0.866f * 5f, 0f) *
                Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(120f));
            _mpMatrix = _modelMatrix * _projMatrix;
            GL.UniformMatrix4(_uMPMatrixLocation, false, ref _mpMatrix);
            GL.DrawArrays(PrimitiveType.TriangleFan, 0, 4);
 
            _modelMatrix =
                Matrix4.CreateScale(5f, 5f, 1f) *
                Matrix4.CreateTranslation(0f, 0.866f * 5f, 0f) *
                Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(240f));
            _mpMatrix = _modelMatrix * _projMatrix;
            GL.UniformMatrix4(_uMPMatrixLocation, false, ref _mpMatrix);
            GL.DrawArrays(PrimitiveType.TriangleFan, 0, 4);
 
            SwapBuffers();
        }
 
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            GL.Viewport(0, 0, Width, Height);
 
            float aspect = (float) Width / Height;
            float worldWidth = aspect * 20f;
            _projMatrix = Matrix4.CreateOrthographic(worldWidth, 20f, 100f, -100f);
        }
    }
}

Вращающийся кубик на шейдерах c Diffuse и Ambient освещением на OpenGL 3 и C#

Проект для VS: RotatingCube_OpenTkOpenGL30CSharp.zip (3.2 МБ) Откроется на Mac в “VS for Mac” и на Linux в MonoDevelop

RotatingCube_OpenTkOpenGL30CSharp

Весь код в одном файле:

Program.cs

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

namespace RotatingCube
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var window = new Window())
            {
                window.Title = "OpenGL 3, C#";
                window.Run(60);
            }
        }
    }

    class Window : GameWindow
    {
        private Matrix4 _projMatrix;
        private Matrix4 _viewMatrix;
        private Matrix4 _modelMatrix;
        private Matrix4 _mvpMatrix;
        private Matrix4 _normalMatrix;
        private int _uMvpMatrixLocation;
        private int _uNormalMatrixLocation;
        private int _amountOfVertices = 0;
        private int _uColor;
        private float _currentAngle = 0f;
        private bool _isUpdating = false;

        public Window() : base(250, 250, new GraphicsMode(32, 24, 0, 8)) { }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            var vShaderSource =
                @"
                    #version 130
 
                    in vec3 aPosition;
                    in vec4 aNormal;
                    uniform mat4 uMvpMatrix;
                    uniform mat4 uNormalMatrix;
                    out float vNdotL;
 
                    void main()
                    {
                        gl_Position = uMvpMatrix * vec4(aPosition, 1.0);

                        vec4 normal = uNormalMatrix * aNormal;
                        vec3 lightDir = vec3(1, 5, 3);
                        vNdotL = max(dot(normalize(normal.xyz), normalize(lightDir)), 0.0);
                    }
                ";
            var fShaderSource =
                @"
                    #version 130
                    precision mediump float;

                    in float vNdotL;
                    uniform vec4 uColor;
                    out vec4 fragColor;
 
                    void main()
                    {
                        vec3 diffuseLight = vec3(1.0, 1.0, 1.0);
                        vec3 diffuseColor = diffuseLight * uColor.rgb * vNdotL;

                        vec3 ambientLight = vec3(0.2, 0.2, 0.2);
                        vec3 ambientColor = ambientLight * uColor.rgb;

                        fragColor = vec4(diffuseColor + ambientColor, uColor.a);
                    }
                ";
            var vShader = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vShader, vShaderSource);
            GL.CompileShader(vShader);
            Console.WriteLine(GL.GetShaderInfoLog(vShader));
            var fShader = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fShader, fShaderSource);
            GL.CompileShader(fShader);
            Console.WriteLine(GL.GetShaderInfoLog(fShader));
            var program = GL.CreateProgram();
            GL.AttachShader(program, vShader);
            GL.AttachShader(program, fShader);
            GL.LinkProgram(program);
            GL.UseProgram(program);

            _amountOfVertices = InitVertexBuffers(program);

            _uMvpMatrixLocation = GL.GetUniformLocation(program, "uMvpMatrix");
            _uNormalMatrixLocation = GL.GetUniformLocation(program, "uNormalMatrix");
            _uColor = GL.GetUniformLocation(program, "uColor");
            GL.Uniform4(_uColor, 1f, 0f, 0f, 1f);

            _viewMatrix = Matrix4.LookAt(
                eye: new Vector3(3f, 7f, 10f),
                target: new Vector3(0f, 0f, 0f),
                up: new Vector3(0f, 1f, 0f));
            _modelMatrix = Matrix4.Identity;
            _normalMatrix = Matrix4.Identity;

            GL.ClearColor(0f, 0f, 0f, 1f);
            GL.Enable(EnableCap.DepthTest);
        }

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

            if (!_isUpdating)
            {
                return;
            }

            _currentAngle = _currentAngle + 80f * (float)e.Time;
            _currentAngle %= 360;

            _modelMatrix =
                Matrix4.CreateScale(2f, 2f, 2f) *
                Matrix4.CreateRotationY(MathHelper.DegreesToRadians(_currentAngle));
            _mvpMatrix = _modelMatrix * _viewMatrix * _projMatrix;

        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);
            if (!_isUpdating)
            {
                _isUpdating = true;
            }
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            //Matrix4.Invert(ref _modelMatrix, out _normalMatrix);
            Matrix4.Invert(_modelMatrix);
            Matrix4.Transpose(_modelMatrix);
            GL.UniformMatrix4(_uNormalMatrixLocation, false, ref _modelMatrix);
            GL.UniformMatrix4(_uMvpMatrixLocation, false, ref _mvpMatrix);
            GL.DrawElements(PrimitiveType.Triangles, _amountOfVertices, DrawElementsType.UnsignedInt, 0);

            SwapBuffers();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            GL.Viewport(0, 0, Width, Height);

            float aspect = (float)Width / Height;
            _projMatrix = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(40f), aspect, 0.1f, 100f);
        }

        private int InitVertexBuffers(int program)
        {
            // Create a cube
            //    v6----- v5
            //   /|      /|
            //  v1------v0|
            //  | |     | |
            //  | |v7---|-|v4
            //  |/      |/
            //  v2------v3

            float[] vertices = new float[]
            {
               1f, 1f, 1f, -1f, 1f, 1f, -1f, -1f, 1f, 1f, -1f, 1f,      // v0-v1-v2-v3 front
               1f, 1f, 1f, 1f, -1f, 1f, 1f, -1f, -1f, 1f, 1f, -1f,      // v0-v3-v4-v5 right
               1f, 1f, 1f, 1f, 1f, -1f, -1f, 1f, -1f, -1f, 1f, 1f,      // v0-v5-v6-v1 up
              -1f, 1f, 1f, -1f, 1f, -1f, -1f, -1f, -1f, -1f, -1f, 1f,   // v1-v6-v7-v2 left
              -1f, -1f, -1f, 1f, -1f, -1f, 1f, -1f, 1f, -1f, -1f, 1f,   // v7-v4-v3-v2 down
               1f, -1f, -1f, -1f, -1f, -1f, -1f, 1f, -1f, 1f, 1f, -1f   // v4-v7-v6-v5 back
            };

            float[] normals = new float[]
            {
                0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f,     // v0-v1-v2-v3 front
                1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f,     // v0-v3-v4-v5 right
                0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 0f,     // v0-v5-v6-v1 up
                -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, // v1-v6-v7-v2 left
                0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, // v7-v4-v3-v2 down
                0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f, 0f, 0f, -1f  // v4-v7-v6-v5 back
            };

            int[] indices = new int[]
            {
                0, 1, 2, 0, 2, 3,       // front
                4, 5, 6, 4, 6, 7,       // right
                8, 9, 10, 8, 10, 11,    // up
                12, 13, 14, 12, 14, 15, // left
                16, 17, 18, 16, 18, 19, // down
                20, 21, 22, 20, 22, 23  // back
            };

            InitArrayBuffer(program, vertices, "aPosition");
            InitArrayBuffer(program, normals, "aNormal");

            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

            int indexBuffer;
            GL.CreateBuffers(1, out indexBuffer);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
            GL.BufferData(BufferTarget.ElementArrayBuffer, sizeof(int) * indices.Length, indices, BufferUsageHint.StaticDraw);

            return indices.Length;
        }

        private void InitArrayBuffer(int program, float[] data, string attributeName)
        {
            int vbo;
            GL.CreateBuffers(1, out vbo);

            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            GL.BufferData(BufferTarget.ArrayBuffer, sizeof(float) * data.Length, data, BufferUsageHint.StaticDraw);
            int attributeLocation = GL.GetAttribLocation(program, attributeName);
            GL.VertexAttribPointer(attributeLocation, 3, VertexAttribPointerType.Float, false, 0, 0);
            GL.EnableVertexAttribArray(attributeLocation);
        }
    }
}