Список работ

C#, OpenGL: вывод стандартных примитивов

Содержание

Введение

Описывается C#-программа, воспроизводящая средствами taoFramework-библиотек (DevIl.dll, OpenGl.dll, FreeGlut.dll и Platform.Windows.dll) следующие стандартные примитивы:

Объекты представлены в библиотеке FreeGlut как полигональные модели. Их можно отобразить в виде каркаса (glutWire) либо залитыми цветом (glutSolid) или текстурой (glutSolid + текстура).
Программа обеспечивает вывод любого из перечисленных выше представлений.
При проецировании используется изометрия, получаемая в результате умножения единичной матрицы на матрицы поворота по часовой стрелки на 30° и против нее на 45° соответственно вокруг осей X и Y:

            // Матрица проецирования
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Gl.glRotated(-30, 1, 0, 0);
            Gl.glRotated(45, 0, 1, 0);

Видовая матрица преимущественно является единичной:

            // Видовая матрица
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();

При выводе додекаэдра единичная видовая матрица умножается на матрицу масштабирования:

            Gl.glScaled(0.5, 0.5, 0.5);

При выводе тонированных тел (glutSolid) используется модель освещенности с одним источником света.
Нормали к glut-объектам генерируются автоматически.
Заметим, что Платоновы тела выводятся без сглаживания, даже если указан

            Gl.glShadeModel(Gl.GL_SMOOTH); // Вывод с интерполяцией цветов

В случае текстурирования для создания текстуры используется приведенное на рис. 1 изображение.

Создаем текстуру

Рис. 1. Рисунок для текстуры

Вдобавок с использованием OpenTK-OpenGL-GLControl выводится куб.

Реализация программы вывода примитивов библиотеки Glut

Дополнительно требуется подключение следующих taoFramework-библиотек.

Порядок подключения библиотек описан, например, в работе Программная имитация эксперимента по определению ускорения свободного падения.
Там же можно посмотреть, как ввести в форму область графического вывода simpleOpenGlControl.
В рассматриваемой программе эта область имеет имя watch.
Если же после подключения taoFramework-библиотек при запуске программы не определяется положение Tao.DevIl.dll, то выполните Мой компьютер - Свойства - Дополнительные параметры системы - Переменные среды - в списках системных переменных выберите Path - Изменить. Далее в конце имеющегося списка путей поставьте точку с запятой и добавьте путь к TaoFramework\bin, а вслед аналогичным образом добавьте путь к TaoFramework\lib, например:

C:\Program Files (x86)\TaoFramework\bin; C:\Program Files (x86)\TaoFramework\lib;

Интерфейс пользователя реализован в виде приведенной на рис. 2 формы.

Форма приложения

Рис. 2. Интерфейс пользователя

Примитив выводится после нажатия на одну из кнопок.
В зависимости от положения переключателя (Wire, Solid или Texture) отображается либо каркасная, либо тоновая, либо текстурированная модель (рис. 3).

Чайники

Рис. 3. Три способа вывода примитива

Полный код формы приложения:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
// OpenGL
using Tao.DevIl;
using Tao.OpenGl;
using Tao.FreeGlut;
using Tao.Platform.Windows; // SimpleOpenGLControl

namespace watch
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            // Инициализация контекста окна графического вывода
            watch.InitializeContexts();
        }
        private void showSolid(int obj)
        {
            switch (obj)
            {
                case 1:
                    Glut.glutSolidCone(0.2, 0.75, 16, 8); break; // Конус
                case 2:
                    Glut.glutSolidCube(0.75); break; // Куб
                case 3:
                    Glut.glutSolidCylinder(0.2, 0.75, 16, 16); break; // Цилиндр
                case 4:
                    Gl.glScaled(0.5, 0.5, 0.5);
                    Glut.glutSolidDodecahedron(); break; // Додекаэдр
                case 5:
                    Glut.glutSolidIcosahedron(); break; // Икосаэдр
                case 6:
                    Glut.glutSolidOctahedron(); break; // Октаэдр
                case 7:
                    Glut.glutSolidRhombicDodecahedron(); break; // Ромбический додекаэдр
                case 8:
                    double[] offset = { 0.0 };
                    Glut.glutSolidSierpinskiSponge(7, offset, 1); break; // Фрактал Губка Серпиского
                case 9:
                    Glut.glutSolidSphere(0.75, 16, 16); break; // Сфера
                case 10:
                    Glut.glutSolidTeapot(0.5); break; // Чайник
                case 11:
                    Gl.glRotated(180, 0, 1, 0);
                    Glut.glutSolidTetrahedron(); break; // Тетраэдр
                case 12:
                    Glut.glutSolidTorus(0.15, 0.65, 16, 16); break; // Тор
            }
        }
        private void draw(int obj)
        {
            // Очистка буфера цвета и буфера глубины
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT | Gl.GL_ACCUM_BUFFER_BIT);
            // Матрица проецирования
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Gl.glRotated(-30, 1, 0, 0);
            Gl.glRotated(45, 0, 1, 0);
            // Видовая матрица
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
            if (radioButtonWire.Checked)
            {
                // Белый цвет
                Gl.glColor3f(1, 1, 1);
                // Выводим glut-примитив в виде каркаса
                switch (obj)
                {
                    case 1:
                        Glut.glutWireCone(0.2, 0.75, 16, 8); break; // Конус
                    case 2:
                        Glut.glutWireCube(0.75); break; // Куб
                    case 3:
                        Glut.glutWireCylinder(0.2, 0.75, 16, 16); break; // Цилиндр
                    case 4:
                        Gl.glScaled(0.5, 0.5, 0.5);
                        Glut.glutWireDodecahedron(); break; // Додекаэдр
                    case 5:
                        Glut.glutWireIcosahedron(); break; // Икосаэдр
                    case 6:
                        Glut.glutWireOctahedron(); break; // Октаэдр
                    case 7:
                        Glut.glutWireRhombicDodecahedron(); break; // Ромбический додекаэдр
                    case 8:
                        double[] offset = { 0, 0, 0 };
                        Glut.glutWireSierpinskiSponge(7, offset, 1); break; // Фрактал Губка Серпиского
                    case 9:
                        Glut.glutWireSphere(0.75, 16, 16); break; // Сфера
                    case 10:
                        Glut.glutWireTeapot(0.5); break; // Чайник
                    case 11:
                        Gl.glRotated(180, 0, 1, 0);
                        Glut.glutWireTetrahedron(); break; // Тетраэдр
                    case 12:
                        Glut.glutWireTorus(0.15, 0.65, 16, 16); break; // Тор
                }
            }
            else if (radioButtonSolid.Checked)
            {
                // Модель освещенности с одним источником цвета
                float[] light_position = { 10, 10, -30, 0 }; // Координаты источника света
                float[] lghtClr = { 1, 1, 1, 0 }; // Источник излучает белый цвет
                float[] mtClr = { 0, 1, 0, 0 }; // Материал зеленого цвета
                Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_FILL); // Заливка полигонов
                Gl.glShadeModel(Gl.GL_SMOOTH); // Вывод с интерполяцией цветов
                Gl.glEnable(Gl.GL_LIGHTING); // Будем рассчитывать освещенность
                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, light_position);
                Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, lghtClr); // Рассеивание
                Gl.glEnable(Gl.GL_LIGHT0); // Включаем в уравнение освещенности источник GL_LIGHT0
                // Диффузионная компонента цвета материала
                Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_DIFFUSE, mtClr);
                // Выводим тонированный glut-примитив
                showSolid(obj);
                Gl.glDisable(Gl.GL_LIGHTING); // Будем рассчитывать освещенность
            }
            else if (radioButtonTexture.Checked)
            {
                Gl.glEnable(Gl.GL_TEXTURE_2D);
                // Образ см. на рис. 1
                string path = "C:\\100byte_\\stdntswrks\\cshrp\\stdprmtvs\\sun.jpg";
                uint mGlTextureObject = 0;
                // Загрузка образа для текстуры
                int texid = 0;
                Il.ilGenImages(1, out texid);
                Il.ilBindImage(texid);
                bool success = Il.ilLoadImage(path);
                if (success)
                {
                    int width = Il.ilGetInteger(Il.IL_IMAGE_WIDTH);
                    int height = Il.ilGetInteger(Il.IL_IMAGE_HEIGHT);
                    // Число бит на пиксель
                    int bitspp = Il.ilGetInteger(Il.IL_IMAGE_BITS_PER_PIXEL);
                    switch (bitspp)
                    {
                        // Создаем текстуру, используя GL_RGB или GL_RGBA
                        case 24:
                            mGlTextureObject = MakeGlTexture(Gl.GL_RGB, Il.ilGetData(), width, height); break;
                        case 32:
                            mGlTextureObject = MakeGlTexture(Gl.GL_RGBA, Il.ilGetData(), width, height); break;
                    }
                    Il.ilDeleteImage(texid);
                }
                else
                {
                    MessageBox.Show("ERROR");
                    return;
                }
                // Активизируем генерацию координат текстуры
                Gl.glEnable(Gl.GL_TEXTURE_GEN_S);
                Gl.glEnable(Gl.GL_TEXTURE_GEN_T);
                if (obj == 1 || obj == 4) // Конус или додекаэдр
                {
                    Gl.glTexGeni(Gl.GL_S, Gl.GL_TEXTURE_GEN_MODE, Gl.GL_OBJECT_LINEAR);
                    Gl.glTexGeni(Gl.GL_T, Gl.GL_TEXTURE_GEN_MODE, Gl.GL_OBJECT_LINEAR);
                }
                else
                {
                    Gl.glTexGeni(Gl.GL_S, Gl.GL_TEXTURE_GEN_MODE, Gl.GL_SPHERE_MAP); // GL_OBJECT_LINEAR, GL_EYE_LINEAR
                    Gl.glTexGeni(Gl.GL_T, Gl.GL_TEXTURE_GEN_MODE, Gl.GL_SPHERE_MAP);
                }
                // Выводим текстурированный glut-примитив
                showSolid(obj);
                Gl.glDisable(Gl.GL_TEXTURE_GEN_S);
                Gl.glDisable(Gl.GL_TEXTURE_GEN_T);
                Gl.glDisable(Gl.GL_TEXTURE_2D);
                Gl.glDeleteTextures(1, ref mGlTextureObject);
            }
            watch.Invalidate();
        }
        // Создает текстуру и возвращает идентификатор созданного объект
        private static uint MakeGlTexture(int Format, IntPtr pixels, int w, int h)
        {
            // Идентификатор текстуры
            uint texObject;
            // Генерируем под номером 1 текстуру
            Gl.glGenTextures(1, out texObject);
            // Выполняем привязка OpenGL к созданной текстуре
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, texObject);
            // Режимы фильтрации текстуры
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
            // Замещаем существующую заливку
            Gl.glTexEnvf(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_REPLACE);
            // Создаем RGB или RGBA текстуру (в зависимости от значения параметра Format)
            Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Format, w, h, 0, Format, Gl.GL_UNSIGNED_BYTE, pixels);
            // Возвращаем идентификатор текстуры
            return texObject;
        }
        private void buttonCone_Click(object sender, EventArgs e)
        {
            draw(1); // Конус
        }
        private void buttonCube_Click(object sender, EventArgs e)
        {
            draw(2); // Куб
        }
        private void buttonCylinder_Click(object sender, EventArgs e)
        {
            draw(3); // Цилиндр
        }
        private void buttonDodecahedron_Click(object sender, EventArgs e)
        {
            draw(4); // Додекаэдр
        }
        private void buttonIcosahedron_Click(object sender, EventArgs e)
        {
            draw(5); // Икосаэдр
        }
        private void buttonOctahedron_Click(object sender, EventArgs e)
        {
            draw(6); // Октаэдр
        }
        private void buttonRhombicDodecahedron_Click(object sender, EventArgs e)
        {
            draw(7); // Ромбический додекаэдр
        }
        private void buttonSierpinskiSponge_Click(object sender, EventArgs e)
        {
            draw(8); // Фрактал Губка Серпиского
        }
        private void buttonSphere_Click(object sender, EventArgs e)
        {
            draw(9); // Сфера
        }
        private void buttonTeapot_Click(object sender, EventArgs e)
        {
            draw(10); // Чайник
        }
        private void buttonTetrahedron_Click(object sender, EventArgs e)
        {
            draw(11); // Тетраэдр
        }
        private void buttonTorus_Click(object sender, EventArgs e)
        {
            draw(12); // Тор
        }
        // Обработчик загрузки области графического вывода
        private void watch_Load(object sender, EventArgs e)
        {
            // Черный цвет фона
            Gl.glClearColor(0, 0, 0, 1);
            // Инициализация Il
            Il.ilInit();
            Il.ilEnable(Il.IL_ORIGIN_SET);
            // Инициализация Glut
            Glut.glutInit();
            // Используем в Glut систему цветов RGB, двойную буферизацию (экранный и внеэкранный буферы) и буфер глубины
            Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE | Glut.GLUT_DEPTH);
            // Активизируем тест глубины
            Gl.glEnable(Gl.GL_DEPTH_TEST);
        }
    }
}

Вывод куба средствами OpenTK

Форма (рис. 4) обеспечивает вывод и поворот куба относительно осей координат.

Куб

Рис. 4. Форма, обеспечивающая вывод и поворот куба

using System;
using System.Drawing;
using System.Windows.Forms;
using OpenTK; // WindowState , Exit(), Vector3d and so on
using OpenTK.Graphics.OpenGL;

namespace WindowsFormsApplicationCubeRot
{
    public partial class FormCubeRot : Form
    {
        double crds = 45, edgeLen = 5;
        public FormCubeRot()
        {
            InitializeComponent();
        }
        private void buttonClose_Click(object sender, EventArgs e)
        {
            Close();
        }
        private void clear()
        {
            GL.ClearColor(Color.Beige);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        }
        private void coords()
        {
            GL.LineWidth(1);
            GL.Begin(PrimitiveType.Lines);
            GL.Color3(Color.Red);
            GL.Vertex3(0, 0, 0);
            GL.Vertex3(crds, 0, 0);
            GL.Color3(Color.Green);
            GL.Vertex3(0, 0, 0);
            GL.Vertex3(0, crds, 0);
            GL.Color3(Color.Blue);
            GL.Vertex3(0, 0, 0);
            GL.Vertex3(0, 0, crds);
            GL.End();
        }

        private void cube(double xCube, double yCube)
        {
            double[] vLeftFarBot = { -edgeLen + xCube, -edgeLen + yCube, -edgeLen };
            double[] vLeftNearBot = { -edgeLen + xCube, -edgeLen + yCube, edgeLen };
            double[] vRightFarBot = { edgeLen + xCube, -edgeLen + yCube, -edgeLen };
            double[] vRightNearBot = { edgeLen + xCube, -edgeLen + yCube, edgeLen };
            double[] vLeftFarTop = { -edgeLen + xCube, edgeLen + yCube, -edgeLen };
            double[] vLeftNearTop = { -edgeLen + xCube, edgeLen + yCube, edgeLen };
            double[] vRightFarTop = { edgeLen + xCube, edgeLen + yCube, -edgeLen };
            double[] vRightNearTop = { edgeLen + xCube, edgeLen + yCube, edgeLen };
            GL.LineWidth(2);
            GL.Begin(PrimitiveType.Quads);
            // Bottom
            GL.Color3(Color.DarkGreen);
            GL.Normal3(0.0, -1.0, 0.0);
            GL.Vertex3(vLeftNearBot);
            GL.Vertex3(vLeftFarBot);
            GL.Vertex3(vRightFarBot);
            GL.Vertex3(vRightNearBot);
            // Top
            GL.Normal3(0.0, 1.0, 0.0);
            GL.Color3(Color.DarkMagenta);
            GL.Vertex3(vLeftNearTop);
            GL.Vertex3(vRightNearTop);
            GL.Vertex3(vRightFarTop);
            GL.Vertex3(vLeftFarTop);
            // Front
            GL.Normal3(0.0, 0.0, 1.0);
            GL.Color3(Color.Yellow);
            GL.Vertex3(vLeftNearBot);
            GL.Vertex3(vRightNearBot);
            GL.Vertex3(vRightNearTop);
            GL.Vertex3(vLeftNearTop);
            // Back
            GL.Normal3(0.0, 0.0, -1.0);
            GL.Color3(Color.Blue);
            GL.Vertex3(vLeftFarBot);
            GL.Vertex3(vLeftFarTop);
            GL.Vertex3(vRightFarTop);
            GL.Vertex3(vRightFarBot);
            // Left
            GL.Normal3(-1.0, 0.0, 0.0);
            GL.Color3(Color.Red);
            GL.Vertex3(vLeftFarBot);
            GL.Vertex3(vLeftNearBot);
            GL.Vertex3(vLeftNearTop);
            GL.Vertex3(vLeftFarTop);
            // Right
            GL.Normal3(1.0, 0.0, 0.0);
            GL.Color3(Color.Brown);
            GL.Vertex3(vRightNearBot);
            GL.Vertex3(vRightFarBot);
            GL.Vertex3(vRightFarTop);
            GL.Vertex3(vRightNearTop);
            GL.End();
        }

        private void glControlCubeRot_Load(object sender, EventArgs e)
        {
            int Width = glControlCubeRot.Width;
            int Height = glControlCubeRot.Height;
            GL.Viewport(0, 0, Width, Height);
            //
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(-crds, crds, -crds, crds, -crds, crds);
            GL.Rotate(15, new Vector3d(0, 1, 0));
            GL.Rotate(-55, new Vector3d(1, 0, 0));
            GL.Translate(-10, 0, 0);
            //
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
            //
            GL.PolygonMode(MaterialFace.Front, PolygonMode.Fill);
            GL.PolygonMode(MaterialFace.Back, PolygonMode.Point);
            GL.PointSize(8);
            GL.Enable(EnableCap.DepthTest);
            //
            glControlCubeRot.Invalidate();
        }

        private void glControlCubeRot_Paint(object sender, PaintEventArgs e)
        {
            clear();
            coords();
            cube(0, 0);
            glControlCubeRot.SwapBuffers();
        }

        private void buttonRotX_Click(object sender, EventArgs e)
        {
            GL.Rotate(3, 1, 0, 0);
            glControlCubeRot.Invalidate();
        }
        private void buttonRotY_Click(object sender, EventArgs e)
        {
            GL.Rotate(3, 0, 1, 0);
            glControlCubeRot.Invalidate();
        }
        private void buttonRotZ_Click(object sender, EventArgs e)
        {
            GL.Rotate(3, 0, 0, 1);
            glControlCubeRot.Invalidate();
        }
    }
}

Заключение

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

Литература

  1. Боресков А. В. Графика трехмерной компьютерной игры на основе OpenGL. М.: ДИАЛОГ-МИФИ, 2004.

Список работ

Рейтинг@Mail.ru