Описывается C#-программа, воспроизводящая средствами taoFramework-библиотек (DevIl.dll, OpenGl.dll, FreeGlut.dll и Platform.Windows.dll) следующие стандартные примитивы:
Объекты представлены в библиотеке FreeGlut как полигональные модели. Их можно отобразить в виде каркаса (glutWire) либо залитыми цветом (glutSolid) или текстурой (glutSolid + текстура).
Программа обеспечивает вывод любого из перечисленных выше представлений.
При проецировании используется изометрия, получаемая в результате умножения единичной матрицы на матрицы поворота по часовой стрелки на 30° и против нее на 45° соответственно вокруг осей X и Y:
Видовая матрица преимущественно является единичной:
// Видовая матрицаПри выводе додекаэдра единичная видовая матрица умножается на матрицу масштабирования:
Gl.glScaled(0.5, 0.5, 0.5);При выводе тонированных тел (glutSolid) используется модель освещенности с одним источником света.
Нормали к glut-объектам генерируются автоматически.
Заметим, что Платоновы тела выводятся без сглаживания, даже если указан
В случае текстурирования для создания текстуры используется приведенное на рис. 1 изображение.
Рис. 1. Рисунок для текстуры
Вдобавок с использованием OpenTK-OpenGL-GLControl выводится куб.
Дополнительно требуется подключение следующих 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);
}
}
}
Форма (рис. 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.
Так же примитивы усложненной формы можно получать, модифицируя уже имеющиеся в сцене объекты.