Список работ

C#, OpenGL, OpenTK: построение, текстурирование и тонирование сферы

Содержание

Введение

Приводятся C#-функции, воспроизводящие средствами OpenTK-библиотек (OpenTK, OpenTK.Graphics.OpenGL, OpenTK.Input) сферу (рис. 1 и рис. 2).

Рисунок сферы с нормалями

Рис. 1. Сфера с нормалями

Рисунок сфер без нормалей

Рис. 2. Сфера без нормалей

Сфера создается как полигональный объект с числом сегментов по X и Y соответственно nx и ny.
При выводе сферы формируются нормали к ее вершинам и рассчитываются и задаются координаты текстуры.
Рассматриваются два варианта проекта.
В первом применяется WindowsFormsApplication, в форме которого (см. рис. 1) определено поле графического вывода GLControl библиотеки OpenTK.GLControl.dll.
Ссылка на библиотеку добавляется в Solution Explorre. (Solution Explorre – References – правая кнопка мыши – Add Reference – Browse).
Сам же элемент GLControl добавляется из меню Tools - Choose Toolbox Items - вкладка .NET Framework Components - GLControl.
Если этот элемент в списке отсутствует, то его нужно найти, употребив кнопку Browse.
В этом проекте выводится каркасная модель сферы с нормалями и без них (см. рис. 1 и рис. 2).
Во втором используется консоль-проект с GameWindow, в пространство имен которого добавляются недостающие ссылки (Solution Explorre – References – правая кнопка мыши – Add Reference – Browse):

using System;
using System.Drawing; // Color
using System.Drawing.Imaging; // BitmapData
using OpenTK; // WindowState , Exit() and so on
using OpenTK.Graphics.OpenGL;
using OpenTK.Input; // KeyboardKeyEventArgs

Вид сферы в консоль-проекте определяется следующими переменными:

bool showTexture; // Выводим сферу с текстурой
bool useMaterial; // Используем материал и источники света
bool showNormals; // Показываем нормали

Если все перечисленные переменные равны false, то выводится чисто полигональная модель сферы.

Тонированное изображение будет сглаженным, если переменная

flat = false;

В противном случае вывод полигонов будет выполняться без интерполяции цветов.
Смена варианта вывода сферы обеспечивается клавишами F7, F8 и F9:

Текстура формируется на основе показанного на рис. 3 образа.

Карта планета Земля

Рис. 3. Карта Земли

Сферы с текстурой и материалами сглаженная и без сглаживания показаны на рис. 4.

Рисунки сфер с текстурой и материалами

Рис. 4. Сферы с текстурой и материалами в режимах Smooth и Flat

Построение сферы

Нижеприводимая процедура обеспечивает:

        // Вывод сферы, формирование координат нормалей и текстуры
        // r - радиус сферы
        // nx - число полигонов (сегментов) по X
        // ny - число полигонов (сегментов) по Y
        void sphere(double r, int nx, int ny)
        {
            int ix, iy;
            double x, y, z, sy, cy, sy1, cy1, sx, cx, piy, pix, ay, ay1, ax, tx, ty, ty1, dnx, dny, diy;
            dnx = 1.0 / (double)nx;
            dny = 1.0 / (double)ny;
            // Рисуем полигональную модель сферы, формируем нормали и задаем коодинаты текстуры
            // Каждый полигон - это трапеция. Трапеции верхнего и нижнего слоев вырождаются в треугольники
            GL.Begin(PrimitiveType.QuadStrip);
            piy = Math.PI * dny;
            pix = Math.PI * dnx;
            for (iy = 0; iy < ny; iy++)
            {
                diy = (double)iy;
                ay = diy * piy;
                sy = Math.Sin(ay);
                cy = Math.Cos(ay);
                ty = diy * dny;
                ay1 = ay + piy;
                sy1 = Math.Sin(ay1);
                cy1 = Math.Cos(ay1);
                ty1 = ty + dny;
                for (ix = 0; ix <= nx; ix++)
                {
                    ax = 2.0 * ix * pix;
                    sx = Math.Sin(ax);
                    cx = Math.Cos(ax);
                    x = r * sy * cx;
                    y = r * sy * sx;
                    z = r * cy;
                    tx = (double)ix * dnx;
                    // Координаты нормали в текущей вершине
                    GL.Normal3(x, y, z); // Нормаль направлена от центра
                    // Координаты текстуры в текущей вершине
                    GL.TexCoord2(tx, ty);
                    GL.Vertex3(x, y, z);
                    x = r * sy1 * cx;
                    y = r * sy1 * sx;
                    z = r * cy1;
                    GL.Normal3(x, y, z);
                    GL.TexCoord2(tx, ty1);
                    GL.Vertex3(x, y, z);
                }
            }
            GL.End();
            // Показываем нормали
            if (showNormals)
            {
                double rv = 1.15 * r;
                // Толщина линии, отображающей нормаль
                GL.LineWidth(2);
                GL.Color3(Color.White);
                GL.Begin(PrimitiveType.Lines);
                piy = Math.PI * dny;
                pix = Math.PI * dnx;
                for (iy = 0; iy < ny; iy++)
                {
                    diy = (double)iy;
                    ay = diy * piy;
                    sy = Math.Sin(ay);
                    cy = Math.Cos(ay);
                    ay1 = ay + piy;
                    sy1 = Math.Sin(ay1);
                    cy1 = Math.Cos(ay1);
                    for (ix = 0; ix <= nx; ix++)
                    {
                        ax = 2.0 * ix * pix;
                        sx = Math.Sin(ax);
                        cx = Math.Cos(ax);
                        x = r * sy * cx;
                        y = r * sy * sx;
                        z = r * cy;
                        GL.Vertex3(x, y, z);
                        x = rv * sy * cx;
                        y = rv * sy * sx;
                        z = rv * cy;
                        GL.Vertex3(x, y, z);
                    }
                }
                GL.End();
                GL.LineWidth(1);
                GL.Color3(Color.LightGray);
            }
        }

Формирование текстуры

        // Создает 2D-текстуру на базе растрового образа, загруженного в data
        public void makeTxtr()
        {
            // Активизируем режим вывода текстуры
            GL.Enable(EnableCap.Texture2D);
            // Генерируем идентификатор текстуры
            GL.GenTextures(1, out texture);
            // Связываем текстуру с идентификатором
            GL.BindTexture(TextureTarget.Texture2D, texture);
            // Параметры текстуры
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
            // Создаем текстуру
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
        }

Материал и свет

        // Модель освещенности с одним источником цвета
        // Создает материал и источник света
        public void makeMatAndLight()
        {
            float[] light_position = { 0, -30, 30, 0 }; // Координаты источника света
            float[] lghtClr = { 1, 1, 1, 0 }; // Источник излучает белый цвет
            float[] mtClr = { 0, 1, 0, 0 }; // Материал зеленого цвета
            if (flat)
                GL.ShadeModel(ShadingModel.Flat); // Вывод без интерполяции цветов
            else
                GL.ShadeModel(ShadingModel.Smooth); // Вывод с интерполяцией цветов
            GL.Enable(EnableCap.Lighting); // Будем рассчитывать освещенность
            GL.Light(LightName.Light0, LightParameter.Position, light_position);
            GL.Light(LightName.Light0, LightParameter.Ambient, lghtClr); // Рассеивание
            GL.Enable(EnableCap.Light0); // Включаем в уравнение освещенности источник GL_LIGHT0
            // Диффузионная компонента цвета материала
            GL.Material(MaterialFace.Front, MaterialParameter.Diffuse, mtClr);
        }

Весь код WindowsFormsApplication

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

namespace WindowsFormsApplicationOpenTK
{
    public partial class FormSphere : Form
    {
        bool showNormals = false; // Флаг вывода нормалей
        double sphR = 20; // Радиус сферы
        int nx = 16; // Число разбиений сферы по X и Y
        int ny = 16;
        
        public FormSphere()
        {
            InitializeComponent();
        }
        private void glControlSphere_Load(object sender, EventArgs e)
        {
            int Width = glControlSphere.Width;
            int Height = glControlSphere.Height;
            double crds = 1.45 * sphR;
            radioButtonWire.Checked = true;
            GL.Viewport(0, 0, Width, Height);
            // Формируем матрицу проецирования
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(-crds, crds, -crds, crds, -crds, crds);
            // Умножаем матрицу проецирования на матрицы поворотов вокруг осей Y и X
            GL.Rotate(15, new Vector3d(0, 1, 0));
            GL.Rotate(-55, new Vector3d(1, 0, 0));
            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
            glControlSphere.Invalidate();
        }
        private void glControlSphere_Resize(object sender, EventArgs e)
        {
            glControlSphere.Invalidate();
        }
        private void glControlSphere_Paint(object sender, PaintEventArgs e)
        {
            sphere(sphR, nx, ny);
        }
        private void radioButton_CheckedChanged(object sender, EventArgs e)
        {
            showNormals = !radioButtonWire.Checked;
            glControlSphere.Invalidate();
        }
        private void numericUpDownRadius_ValueChanged(object sender, EventArgs e)
        {
            sphR = (double)numericUpDownRadius.Value;
            glControlSphere.Invalidate();
        }
        private void buttonClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        // Вывод сферы, формирование координат нормалей и текстуры
        // r - радиус сферы
        // nx - число полигонов (сегментов) по X
        // ny - число полигонов (сегментов) по Y
        void sphere(double r, int nx, int ny)
        {
            int ix, iy;
            double x, y, z, sy, cy, sy1, cy1, sx, cx, piy, pix, ay, ay1, ax, tx, ty, ty1, dnx, dny, diy;
            dnx = 1.0 / (double)nx;
            dny = 1.0 / (double)ny;
            // Заливка окна вывода и очистка буфера глубины
            GL.ClearColor(Color.Beige);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            GL.Color3(Color.Black);
            // Рисуем полигональную модель сферы, формируем нормали и задаем коодинаты текстуры
            // Каждый полигон - это трапеция. Трапеции верхнего и нижнего слоев вырождаются в треугольники
            GL.Begin(PrimitiveType.QuadStrip);
            piy = Math.PI * dny;
            pix = Math.PI * dnx;
            for (iy = 0; iy < ny; iy++)
            {
                diy = (double)iy;
                ay = diy * piy;
                sy = Math.Sin(ay);
                cy = Math.Cos(ay);
                ty = diy * dny;
                ay1 = ay + piy;
                sy1 = Math.Sin(ay1);
                cy1 = Math.Cos(ay1);
                ty1 = ty + dny;
                for (ix = 0; ix <= nx; ix++)
                {
                    ax = 2.0 * ix * pix;
                    sx = Math.Sin(ax);
                    cx = Math.Cos(ax);
                    x = r * sy * cx;
                    y = r * sy * sx;
                    z = r * cy;
                    tx = (double)ix * dnx;
                    // Координаты нормали в текущей вершине
                    GL.Normal3(x, y, z); // Нормаль направлена от центра
                    // Координаты текстуры в текущей вершине
                    GL.TexCoord2(tx, ty);
                    GL.Vertex3(x, y, z);
                    x = r * sy1 * cx;
                    y = r * sy1 * sx;
                    z = r * cy1;
                    GL.Normal3(x, y, z);
                    GL.TexCoord2(tx, ty1);
                    GL.Vertex3(x, y, z);
                }
            }
            GL.End();
            // Показываем нормали
            if (showNormals)
            {
                double rv = 1.15 * r;
                // Толщина линии, отображающей нормаль
                GL.LineWidth(1.5f);
                GL.Color3(Color.Red);
                GL.Begin(PrimitiveType.Lines);
                piy = Math.PI * dny;
                pix = Math.PI * dnx;
                for (iy = 0; iy < ny; iy++)
                {
                    diy = (double)iy;
                    ay = diy * piy;
                    sy = Math.Sin(ay);
                    cy = Math.Cos(ay);
                    ay1 = ay + piy;
                    sy1 = Math.Sin(ay1);
                    cy1 = Math.Cos(ay1);
                    for (ix = 0; ix <= nx; ix++)
                    {
                        ax = 2.0 * ix * pix;
                        sx = Math.Sin(ax);
                        cx = Math.Cos(ax);
                        x = r * sy * cx;
                        y = r * sy * sx;
                        z = r * cy;
                        GL.Vertex3(x, y, z);
                        x = rv * sy * cx;
                        y = rv * sy * sx;
                        z = rv * cy;
                        GL.Vertex3(x, y, z);
                    }
                }
                GL.End();
                GL.LineWidth(1);
                GL.Color3(Color.LightGray);
            }
            glControlSphere.SwapBuffers();
        }
    }
}

Весь код консоль-проекта

using System;
using System.Drawing; // Color
using System.Drawing.Imaging; // BitmapData
using OpenTK; // WindowState , Exit() and so on
using OpenTK.Graphics.OpenGL;
using OpenTK.Input; // KeyboardKeyEventArgs

namespace SphereTest
{
    public class SimpleWindow : GameWindow
    {
        double sphR = 2; // Радиус сферы
        // Файл для текcтуры
        static string path = "G:\\Шоя\\watch\\earth3.png"; // earth2 или 3 или 4.png
        static Bitmap bitmap = new Bitmap(path);
        BitmapData data;
        int texture;
        bool showTexture = false;
        bool useMaterial = true;
        bool showNormals = false;
        bool flat = true;

        // 300 * 300 - размер окна вывода
        public SimpleWindow() : base(300, 300)
        {
            KeyDown += Keyboard_KeyDown; // Обработчик нажатий на клавиши клавиатуры
        }
        // Обработчик нажатий на клавиши клавиатуры
        void Keyboard_KeyDown(object sender, KeyboardKeyEventArgs e)
        {
            if (e.Key == Key.Escape) this.Exit();
            if (e.Key == Key.F11)
                this.WindowState = (this.WindowState == WindowState.Fullscreen) ? WindowState.Normal : WindowState.Fullscreen;
            if (e.Key == Key.F7)
                if (showNormals)
                {
                    showNormals = false;
                    showTexture = false;
                }
                else
                {
                    showNormals = true;
                    showTexture = false;
                }
            if (e.Key == Key.F8)
                if (flat)
                {
                    flat = false;
                    useMaterial = true;
                    showTexture = false;
                }
                else
                {
                    flat = true;
                    useMaterial = true;
                    showTexture = false;
                }
            if (e.Key == Key.F9)
                if (showTexture)
                {
                    showTexture = false;
                    showNormals = false;
                    useMaterial = true;
                }
                else
                {
                    showTexture = true;
                    showNormals = false;
                    useMaterial = false;
                }
        }
        protected override void OnLoad(EventArgs e)
        {
            if (showTexture)
                GL.ClearColor(Color.Black);
            else
                GL.ClearColor(Color.DarkBlue);
            Rectangle Rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
            data = bitmap.LockBits(Rect, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            double crds = 1.25 * sphR;
            GL.Viewport(0, 0, Width, Height);
            // Формируем матрицу проецирования
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(-crds, crds, -crds, crds, -crds, crds);
            // Умножаем матрицу проецирования на матрицы поворотов вокруг осей Y и X
            GL.Rotate(15, new Vector3d(0, 1, 0));
            GL.Rotate(-55, new Vector3d(1, 0, 0));
        }
        // Вывод сферы, формирование координат нормалей и текстуры
        // r - радиус сферы
        // nx - число полигонов (сегментов) по X
        // ny - число полигонов (сегментов) по Y
        void sphere(double r, int nx, int ny)
        {
            int ix, iy;
            double x, y, z, sy, cy, sy1, cy1, sx, cx, piy, pix, ay, ay1, ax, tx, ty, ty1, dnx, dny, diy;
            dnx = 1.0 / (double)nx;
            dny = 1.0 / (double)ny;
            // Рисуем полигональную модель сферы, формируем нормали и задаем коодинаты текстуры
            // Каждый полигон - это трапеция. Трапеции верхнего и нижнего слоев вырождаются в треугольники
            GL.Begin(PrimitiveType.QuadStrip);
            piy = Math.PI * dny;
            pix = Math.PI * dnx;
            for (iy = 0; iy < ny; iy++)
            {
                diy = (double)iy;
                ay = diy * piy;
                sy = Math.Sin(ay);
                cy = Math.Cos(ay);
                ty = diy * dny;
                ay1 = ay + piy;
                sy1 = Math.Sin(ay1);
                cy1 = Math.Cos(ay1);
                ty1 = ty + dny;
                for (ix = 0; ix <= nx; ix++)
                {
                    ax = 2.0 * ix * pix;
                    sx = Math.Sin(ax);
                    cx = Math.Cos(ax);
                    x = r * sy * cx;
                    y = r * sy * sx;
                    z = r * cy;
                    tx = (double)ix * dnx;
                    // Координаты нормали в текущей вершине
                    GL.Normal3(x, y, z); // Нормаль направлена от центра
                    // Координаты текстуры в текущей вершине
                    GL.TexCoord2(tx, ty);
                    GL.Vertex3(x, y, z);
                    x = r * sy1 * cx;
                    y = r * sy1 * sx;
                    z = r * cy1;
                    GL.Normal3(x, y, z);
                    GL.TexCoord2(tx, ty1);
                    GL.Vertex3(x, y, z);
                }
            }
            GL.End();
            // Показываем нормали
            if (showNormals)
            {
                double rv = 1.15 * r;
                // Толщина линии, отображающей нормаль
                GL.LineWidth(2);
                GL.Color3(Color.White);
                GL.Begin(PrimitiveType.Lines);
                piy = Math.PI * dny;
                pix = Math.PI * dnx;
                for (iy = 0; iy < ny; iy++)
                {
                    diy = (double)iy;
                    ay = diy * piy;
                    sy = Math.Sin(ay);
                    cy = Math.Cos(ay);
                    ay1 = ay + piy;
                    sy1 = Math.Sin(ay1);
                    cy1 = Math.Cos(ay1);
                    for (ix = 0; ix <= nx; ix++)
                    {
                        ax = 2.0 * ix * pix;
                        sx = Math.Sin(ax);
                        cx = Math.Cos(ax);
                        x = r * sy * cx;
                        y = r * sy * sx;
                        z = r * cy;
                        GL.Vertex3(x, y, z);
                        x = rv * sy * cx;
                        y = rv * sy * sx;
                        z = rv * cy;
                        GL.Vertex3(x, y, z);
                    }
                }
                GL.End();
                GL.LineWidth(1);
                GL.Color3(Color.LightGray);
            }
        }
        // Модель освещенности с одним источником цвета
        // Создает материал и источник света
        public void makeMatAndLight()
        {
            float[] light_position = { 0, -30, 30, 0 }; // Координаты источника света
            float[] lghtClr = { 1, 1, 1, 0 }; // Источник излучает белый цвет
            float[] mtClr = { 0, 1, 0, 0 }; // Материал зеленого цвета
            if (flat)
                GL.ShadeModel(ShadingModel.Flat); // Вывод без интерполяции цветов
            else
                GL.ShadeModel(ShadingModel.Smooth); // Вывод с интерполяцией цветов
            GL.Enable(EnableCap.Lighting); // Будем рассчитывать освещенность
            GL.Light(LightName.Light0, LightParameter.Position, light_position);
            GL.Light(LightName.Light0, LightParameter.Ambient, lghtClr); // Рассеивание
            GL.Enable(EnableCap.Light0); // Включаем в уравнение освещенности источник GL_LIGHT0
            // Диффузионная компонента цвета материала
            GL.Material(MaterialFace.Front, MaterialParameter.Diffuse, mtClr);
        }
        // Создает 2D-текстуру на базе растрового образа, загруженного в data
        public void makeTxtr()
        {
            // Активизируем режим вывода текстуры
            GL.Enable(EnableCap.Texture2D);
            // Генерируем идентификатор текстуры
            GL.GenTextures(1, out texture);
            // Связываем текстуру с идентификатором
            GL.BindTexture(TextureTarget.Texture2D, texture);
            // Параметры текстуры
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
            // Создаем текстуру
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
        }
        // Выполняется при воспроизведении кадра изображения
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            GL.Viewport(0, 0, Width, Height);
            GL.Color3(Color.LightGray);
            GL.Disable(EnableCap.Lighting);
            GL.Enable(EnableCap.DepthTest);
            int nx, ny;
            // Число сегментов в моддели сферы по X и Y
            nx = 32;
            ny = 32;
            if (showTexture || useMaterial)
            {
                GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
                if (useMaterial && flat)
                {
                    nx = 24;
                    ny = 24;
                }
            }
            else
            {
                // Полигоны
                GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
                if (showNormals) // Добавляем нормали
                {
                    nx = 16;
                    ny = 16;
                }
            }
            // Сооздаем текстуру
            if (showTexture) makeTxtr();
            // Сооздаем материал и источник света
            if (useMaterial) makeMatAndLight();
            // Выводим сферу
            sphere(sphR, nx, ny);
            if (showTexture) GL.Disable(EnableCap.Texture2D);
            if (useMaterial) GL.Disable(EnableCap.Lighting);
            this.SwapBuffers();
        }
        // Entry point
        [STAThread]
        public static void Main()
        {
            using (SimpleWindow sphere = new SimpleWindow())
            {
                sphere.Title = "Сфера";
                sphere.Run(30.0, 0.0);
            }
        }
    }
}

Список работ

Рейтинг@Mail.ru