Помню как в 2010м году нашел на этом сайте классные уроки по C#+OpenGL и познакомился с интересными людьми, с которыми мы провели много времени в TeamSpeak и Skype, создавая игры. Я писал в блог какие-то школо-статьи с грамматическими ошибками и сообщество сайта оказывало поддержку: Isaer, Anvi и Bumblebee). Эти статьи даже сейчас не удалили:D.
В школьные времена было крайне интересно разрабатывать игры с нуля, разбираться в тонкостях игровых движков. Повзрослев, романтика пропала. Отличные игровые движки уже всё сделали за нас, писать свой движок не имеет смысла. Игровые студии нацелены на получение денег. Даже свои идеи начать разработку чего-то теперь проходят фильтр прибыльности.
Случайно найдя этот сайт, я удивился, как далеко со временем я ушел от гемдева. По окончанию школы я фрилансил в js/php, чтобы получать какие-то деньги, всё ещё думая о будущем в геймдеве. JavaScript открыл мне Canvas, WebRTC, WebGL. Я продолжал делать демки, а теперь эти технологии полностью вытеснили Flash. Позже в поисках серьёзной работы, я надеялся найти что-то в гемдеве и моим тестовым заданием было сделать однорукого бандита из казино:|. Тогда удалось найти оффер поперспективнее и я начал дальше углубляться в веб и бекенд. Окунулся в мир распределённых систем.
Некоторые мои друзья сейчас работают в геймдеве. Не знаю почему так происходит, но их работа - это постоянный стресс (вечные дедлайны и правки). Я чувствую, что судьба помогла мне найти лучший для себя путь.
Опыт с геймдевом стал хорошей базой: практика работы с языком программирования, структуры и алгоритмы, умение дебажить и танцевать с бубном (это вообще самый важный скилл).
Прокрастинатор приветствует! Это первая статья из цикла по разработке мультиплатформенной игры на языке программирования C# с использованием библиотеки OpenTK, которая предоставляет простой доступ к OpenGL. Так же библиотека берет на себя работу с окнами приложения и, таким образом, упрощает нам жизнь при разработке не только под windows, но и linux и mac системы. В дополнение к OpenTK мы будем использовать библиотеки Assimp.Net (порт библиотеки Assimp для С++) для загрузки трехмерных моделей, BEPUphysics для реализации физики и SixLabors для работы с двумерной графикой.
Для начала этого набора нам вполне хватит. Все библиотеки устанавливаются с помощью NuGet и, чуть позже, мы этого коснемся. В качестве текстового редактора я буду использовать Visual Studio Code так как он доступен на всех целевых платформах, бесплатен и его функционала вполне достаточно. Перед тем, как мы начнем, нужно установить SDK для работы с .Net Core. Сделать это можно по это ссылке: https://dotnet.microsoft.com/download Нас интересует LTS версия .Net Core и на момент написания статьи это 3.1. Выбираем ее и устанавливаем. После установки пишем в терминале dotnet и убеждаемся, что все установлено верно. Если же нет - решите вопрос и возвращайтесь к статье когда все будет в порядке.
Если вы, как и я, используете линукс, то быстрее и надежнее будет установить SDK через ваш пакетный менеджер, но убедитесь в том, что вы устанавливаете именно 3.1 версию. Со вступлением покончено, можно приступать к самому интересному. Что бы создать проект нужно проделать несколько простых действий. Для начала, создадим папку, в которой будут храниться все файлы будущей игры. Далее нужно выполнить команду, которая создаст каркас консольного приложения для нас.
dotnet new console
Если вы корректно установили .Net Core, то через несколько секунд программа сообщит вам, что файлы успешно созданы и можно переходить к следующему шагу. Запускаем VS Code и открываем недавно созданную папку.
Следующая наша задача - установка библиотек из NuGet
Делается это довольно просто. Переходим на сайт https://www.nuget.org/ и в строке поиска находим все необходимое. Для упрощения вам жизни я просто опубликую команды, которые нужно ввести в консоли(которую можно вызвать прямо в VS Code):
Версии библиотек актуальны на момент написания статьи. В будущем, когда мы будем обновлять библиотеки, на этом будет акцентировано дополнительное внимание Хорошо. Еще одна настройка перед тем как мы начнем писать код, для того, что бы папка с ресурсами (assets) со всем ее содержимым копировалась при компиляции в папку с исполняемым файлом. Откройте файл с расширением .csproj и вы найдете там тег ItemGroup. В него нужно добавить код, выделенный отступами.
В продолжение перевода с С++ на C#/OpenTk, привожу вторую и последнюю часть цикла уроков. В прошлой заметке всё было усыпано скриншотами, о том как создать и настроить проект, а далее приводился лишь кусок кода. В этот раз будет по большей части код. Основной момент, состоит в том, что вместо GLM из плюсового кода мы используем возможности OpenTk для работы с матрицами.
Код:
using System;
using System.Drawing;
using System.IO;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
namespace openTk2
{
public class GlShader : IDisposable
{
int ShaderProgram;
int vertex_shader;
int fragment_shader;
private void ReleaseUnmanagedResources()
{
GL.UseProgram(0);
GL.DeleteShader(vertex_shader);
GL.DeleteShader(fragment_shader);
GL.DeleteProgram(ShaderProgram);
}
//Освобождение ресурсов шейдера
public void Dispose()
{
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
~GlShader()
{
ReleaseUnmanagedResources();
}
public int loadFiles(string vertex_file_name, string fragment_file_name)
{
vertex_shader = loadSourcefile(vertex_file_name, ShaderType.VertexShader);
fragment_shader = loadSourcefile(fragment_file_name, ShaderType.FragmentShader);
linkProgram();
return ShaderProgram;
}
private int loadSourcefile(string file_name, ShaderType type)
{
var source = File.ReadAllText(file_name);
return compileSource(source, type);
}
public int load(string vertex_source, string fragment_source)
{
vertex_shader = compileSource(vertex_source, ShaderType.VertexShader);
fragment_shader = compileSource(fragment_source, ShaderType.FragmentShader);
linkProgram();
return ShaderProgram;
}
private int compileSource(string source, ShaderType type)
{
//! Создаем фрагментный шейдер
int shader = GL.CreateShader(type);
//! Передаем исходный код
GL.ShaderSource(shader, source);
//! Компилируем шейдер
GL.CompileShader(shader);
return shader;
}
//! Функция печати лога шейдера
private static void ShaderLog(string tag, int shader)
{
var infoLog = "";
GL.GetShaderInfoLog(shader, out infoLog);
if (infoLog.Length > 1)
Console.WriteLine(tag + " InfoLog: " + infoLog + "\n\n\n");
}
private void linkProgram()
{
if (vertex_shader == 0 || fragment_shader == 0)
{
ShaderProgram = 0;
return;
}
ShaderProgram = GL.CreateProgram();
GL.AttachShader(ShaderProgram, vertex_shader);
GL.AttachShader(ShaderProgram, fragment_shader);
GL.LinkProgram(ShaderProgram);
string log = GL.GetProgramInfoLog(ShaderProgram);
if (string.IsNullOrEmpty(log) == false)
Console.WriteLine(log);
}
public void use()
{
GL.UseProgram(ShaderProgram);
}
//---------------------------------------------------
//! Attribute get
public int getAttribLocation(string name)
{
int location = -1;
location = GL.GetAttribLocation(ShaderProgram, name);
if (location == -1)
Console.WriteLine("Could not bind attribute {0}", name);
return location;
}
//! Attribute get
public int getUniformLocation(string name)
{
int location = -1;
location = GL.GetUniformLocation(ShaderProgram, name);
if (location == -1)
Console.WriteLine("Could not bind attribute {0}", name);
return location;
}
public void setUniform(int unifMatrix, Matrix4 matrixProjection)
{
GL.UniformMatrix4(unifMatrix, false, ref matrixProjection);
}
}
class Game : GameWindow
{
private GlShader shader = new GlShader();
//! ID атрибута вершин
int Attrib_vertex;
//! ID атрибута цветов
int Attrib_color;
//! ID юниформ матрицы проекции
int Unif_matrix;
//! ID Vertex Buffer Object
int VBO_vertex;
//! ID Vertex Buffer Object
int VBO_color;
//! ID VBO for element indices
int VBO_element;
//! Вершина
public struct Vertex
{
public float x;
public float y;
public float z;
};
//! Количество индексов
int Indices_count;
//! Матрица проекции
Matrix4 Matrix_projection;
public Game()
: base(640, 480, new GraphicsMode(new ColorFormat(32), 16), "Simple shaders on OpenTK!")
{
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
GL.ClearColor(Color.Black);
//! Инициализация
initGL();
initVBO();
initShader();
}
protected override void OnUnload(EventArgs e)
{
base.OnUnload(e);
//! Освобождение ресурсов
freeVBO();
shader.Dispose();
}
//! Функция печати лога шейдера
static void ShaderLog(string tag, int shader)
{
string infoLog;
GL.GetShaderInfoLog(shader, out infoLog);
if (infoLog.Length > 1)
Console.WriteLine(tag + " InfoLog: " + infoLog + "\n\n\n");
}
//! Проверка ошибок OpenGL, если есть то вывод в консоль тип ошибки
static void CheckOpenGLerror()
{
ErrorCode errCode = GL.GetError();
if (errCode != ErrorCode.NoError)
{
Console.WriteLine("OpenGl error! - {0}", errCode);
Console.WriteLine(System.Environment.StackTrace);
}
}
void initGL()
{
GL.ClearColor(0, 0, 0, 0);
GL.Enable(EnableCap.DepthTest);
}
//! Инициализация шейдеров
private void initShader()
{
//! Исходный код шейдеров
const string vsSource =
@"attribute vec3 coord;
attribute vec3 color;
varying vec3 var_color;
uniform mat4 matrix;
void main() {
gl_Position = matrix * vec4(coord, 1.0);
var_color = color;
}";
const string fsSource =
@"varying vec3 var_color;
void main() {
gl_FragColor = vec4(var_color, 1.0);
}";
if(shader.load(vsSource, fsSource) == -1)
{
Console.WriteLine("error load shader");
return;
}
CheckOpenGLerror();
//! Вытягиваем ID атрибута из собранной программы
Attrib_vertex = shader.getAttribLocation("coord");
CheckOpenGLerror();
//! Вытягиваем ID юниформ
Attrib_color = shader.getAttribLocation("color");
CheckOpenGLerror();
//! Вытягиваем ID юниформ матрицы проекции
Unif_matrix = shader.getUniformLocation("matrix");
CheckOpenGLerror();
}
//! Инициализация VBO_vertex
void initVBO()
{
//! Вершины куба
Vertex [] vertices = {
new Vertex {x = -1.0f,y = -1.0f,z = -1.0f},
new Vertex {x = 1.0f, y =-1.0f, z =-1.0f},
new Vertex {x = 1.0f, y = 1.0f, z =-1.0f},
new Vertex {x = -1.0f,y = 1.0f, z =-1.0f},
new Vertex {x = -1.0f,y = -1.0f,z = 1.0f},
new Vertex {x = 1.0f, y =-1.0f, z = 1.0f},
new Vertex {x = 1.0f, y = 1.0f, z = 1.0f},
new Vertex {x = -1.0f,y = 1.0f,z = 1.0f}
};
//! Цвета куба без альфа компонента(RGB)
Vertex [] colors = {
new Vertex {x = 1.0f,y = 0.5f,z = 1.0f},
new Vertex {x = 1.0f,y = 0.5f,z = 0.5f},
new Vertex {x = 0.5f,y = 0.5f,z = 1.0f},
new Vertex {x = 0.0f,y = 1.0f,z = 1.0f},
new Vertex {x = 1.0f,y = 0.0f,z = 1.0f},
new Vertex {x = 1.0f,y = 1.0f,z = 0.0f},
new Vertex {x = 1.0f,y = 0.0f,z = 1.0f},
new Vertex {x = 0.0f,y = 1.0f,z = 1.0f}
};
//! Индексы вершин, общие и для цветов
uint [] indices = {
0, 4, 5, 0, 5, 1,
1, 5, 6, 1, 6, 2,
2, 6, 7, 2, 7, 3,
3, 7, 4, 3, 4, 0,
4, 7, 6, 4, 6, 5,
3, 0, 1, 3, 1, 2
};
// Создаем буфер для вершин
GL.GenBuffers(1, out VBO_vertex);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO_vertex);
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * Vector3.SizeInBytes, vertices, BufferUsageHint.StaticDraw);
// Создаем буфер для цветов вершин
GL.GenBuffers(1, out VBO_color);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO_color);
GL.BufferData(BufferTarget.ArrayBuffer, colors.Length * Vector3.SizeInBytes, colors, BufferUsageHint.StaticDraw);
// Создаем буфер для индексов вершин
GL.GenBuffers(1, out VBO_element);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBO_element);
GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(int), indices, BufferUsageHint.StaticDraw);
Indices_count = indices.Length;
CheckOpenGLerror();
}
void freeVBO()
{
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
GL.DeleteBuffers(1, ref VBO_element);
GL.DeleteBuffers(1, ref VBO_element);
GL.DeleteBuffers(1, ref VBO_color);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
//! Устанавливаем шейдерную программу текущей
shader.use();
//! Передаем матрицу в шейдер
shader.setUniform(Unif_matrix, Matrix_projection);
//! Подключаем буфер с индексами вершин общий для цветов и их вершин
GL.BindBuffer(BufferTarget.ElementArrayBuffer, VBO_element);
//! ВЕРШИНЫ
//! Включаем массив атрибутов для вершин
GL.EnableVertexAttribArray(Attrib_vertex);
//! Подключаем VBO
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO_vertex);
//! Указывая pointer 0 при подключенном буфере, мы указываем что данные в VBO
GL.VertexAttribPointer(Attrib_vertex, 3, VertexAttribPointerType.Float, false, 0, 0);
//! ЦВЕТА
//! Включаем массив атрибутов для цветов
GL.EnableVertexAttribArray(Attrib_color);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO_color);
GL.VertexAttribPointer(Attrib_color, 3, VertexAttribPointerType.Float, false, 0, 0);
//! Передаем данные на видеокарту(рисуем)
GL.DrawElements(BeginMode.Triangles, Indices_count, DrawElementsType.UnsignedInt, 0);
//! Отключаем массив атрибутов
GL.DisableVertexAttribArray(Attrib_vertex);
//! Отключаем массив атрибутов
GL.DisableVertexAttribArray(Attrib_color);
CheckOpenGLerror();
SwapBuffers();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
var height = ClientRectangle.Height > 0 ? ClientRectangle.Height : 1;
float aspectRatio = (float)ClientRectangle.Width/(float)height;
Matrix_projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(45.0f), aspectRatio, 1.0f, 200.0f);
// Перемещаем центр нашей оси координат для того чтобы увидеть куб
Matrix_projection = Matrix4.Mult(Matrix4.CreateTranslation(new Vector3(0.0f, 0.0f, -10.0f)), Matrix_projection);
// Поворачиваем ось координат(тоесть весь мир), чтобы развернуть отрисованное
Matrix_projection = Matrix4.Mult(Matrix4.CreateFromAxisAngle(new Vector3(1.0f, 1.0f, 0.0f), MathHelper.DegreesToRadians(60.0f)), Matrix_projection);
}
}
class Program
{
static void Main(string[] args)
{
var g = new Game();
g.Run(30.0f);
}
}
}
Этим кодом можно заменить код из прошлой заметки, он должен компилироваться также хорошо. C# вариант класса для шейдеров реализует интерфейс IDisposable для правильного освобождения ресурсов шейдерной программы. Освобождение происходит по событию выгрузки класса Game OnUnload. Для трансформации и поворота матриц применяются методы из класса Matrix4, которые есть в OpenTk.
Результат работы:
Впрочем он должен быть такой же, как в оригинальном плюсовом коде.
Когда-то я ознакомился с уроком "Простой шейдер на GLSL", и это вдохновило на изучение шейдеров в OpenGL и сам современный OpenGL в частности. В основном я работаю с C# поэтому после ознакомления с кодом на C++ и его сборкой, приступил к переводу его в С#. Эта заметка как раз о том, как перенести код из урока на C#. Для доступа к OpenGL будет использоваться современная версия враппера OpenTK.
И так сперва создадим консольный проект на C#:
Подключим OpenTk через nuget
если не нашло OpenTk то нужно включить репоизоторий в настройках:
Используем следующий код:
using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
namespace openTk1
{
class Game : GameWindow
{
//! Переменные с индентификаторами ID
//! ID шейдерной программы
int Program;
//! ID атрибута
int Attrib_vertex;
//! ID юниформ переменной цвета
int Unif_color;
//! ID Vertex Buffer Object
int VBO;
//! Вершина
struct Vertex
{
public float x;
public float y;
};
public Game()
: base(640, 480, new GraphicsMode(new ColorFormat(32), 16), "Simple shaders on OpenTK!")
{
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
GL.ClearColor(Color.Black);
//! Инициализация
InitVBO();
InitShader();
}
protected override void OnUnload(EventArgs e)
{
base.OnUnload(e);
//! Освобождение ресурсов
FreeShader();
FreeVBO();
}
//! Функция печати лога шейдера
static void ShaderLog(string tag, int shader)
{
string infoLog;
GL.GetShaderInfoLog(shader, out infoLog);
if (infoLog.Length > 1)
Console.WriteLine(tag + " InfoLog: " + infoLog + "\n\n\n");
}
//! Проверка ошибок OpenGL, если есть то вывод в консоль тип ошибки
static void CheckOpenGLerror()
{
ErrorCode errCode = GL.GetError();
if (errCode != ErrorCode.NoError)
Console.WriteLine("OpenGl error! - {0}", errCode);
}
private void InitShader()
{
//! Исходный код шейдеров
const string vsSource = @"attribute vec2 coord;
void main() {
gl_Position = vec4(coord, 0.0, 1.0);
};";
const string fsSource = @"uniform vec4 color;
void main() {
gl_FragColor = color;
}";
//! Переменные для хранения идентификаторов шейдеров
int vShader, fShader;
//! Создаем вершинный шейдер
vShader = GL.CreateShader(ShaderType.VertexShader);
//! Передаем исходный код
GL.ShaderSource(vShader, vsSource);
//! Компилируем шейдер
GL.CompileShader(vShader);
ShaderLog("vertex shader", vShader);
//! Создаем фрагментный шейдер
fShader = GL.CreateShader(ShaderType.FragmentShader);
//! Передаем исходный код
GL.ShaderSource(fShader, fsSource);
//! Компилируем шейдер
GL.CompileShader(fShader);
ShaderLog("fragment shader", fShader);
//! Создаем программу и прикрепляем шейдеры к ней
Program = GL.CreateProgram();
GL.AttachShader(Program, vShader);
GL.AttachShader(Program, fShader);
//! Линкуем шейдерную программу
GL.LinkProgram(Program);
//Единственное, что не перевел на OpenTK:
//! Проверяем статус сборки
//int link_ok;
//GL.GetProgramiv(Program, GL_LINK_STATUS, &link_ok);
//if(!link_ok)
//{
// std::cout << "error attach shaders \n";
// return;
//}
//! Вытягиваем ID атрибута из собранной программы
const string attr_name = "coord";
Attrib_vertex = GL.GetAttribLocation(Program, attr_name);
if (Attrib_vertex == -1)
{
Console.WriteLine("could not bind attrib {0}", attr_name);
return;
}
//! Вытягиваем ID юниформ
const string unif_name = "color";
Unif_color = GL.GetUniformLocation(Program, unif_name);
if (Unif_color == -1)
{
Console.WriteLine("could not bind uniform {0}", unif_name);
return;
}
CheckOpenGLerror();
}
//! Инициализация VBO
void InitVBO()
{
GL.GenBuffers(1, out VBO);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
//! Вершины нашего треугольника
Vertex[] triangle =
{
new Vertex {x = -1.0f, y = -1.0f},
new Vertex {x = 0.0f, y = 1.0f},
new Vertex {x = 1.0f, y = -1.0f}
};
//! Передаем вершины в буфер, 8 - количество байт в структуре 4 байта на 1 float, их у нас 2
GL.BufferData(BufferTarget.ArrayBuffer, triangle.Length * 8, triangle, BufferUsageHint.StaticDraw);
CheckOpenGLerror();
}
//! Освобождение шейдеров
void FreeShader()
{
//! Передавая ноль, мы отключаем шейдрную программу
GL.UseProgram(0);
//! Удаляем шейдерную программу
GL.DeleteProgram(Program);
}
//! Освобождение шейдеров
void FreeVBO()
{
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.DeleteBuffers(1, ref VBO);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit);
//! Устанавливаем шейдерную программу текущей
GL.UseProgram(Program);
//! Передаем юниформ в шейдер
GL.Uniform4(Unif_color, 1.0f, 0.0f, 0.0f, 1.0f);
//! Включаем массив атрибутов
GL.EnableVertexAttribArray(Attrib_vertex);
//! Подключаем VBO
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
//! Указывая pointer 0 при подключенном буфере, мы указываем что данные в VBO
GL.VertexAttribPointer(Attrib_vertex, 2, VertexAttribPointerType.Float, false, 0, 0);
//! Отключаем VBO
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
//! Передаем данные на видеокарту(рисуем)
GL.DrawArrays(PrimitiveType.Triangles, 0, 3); // 3 вершины
//! Отключаем массив атрибутов
GL.DisableVertexAttribArray(Attrib_vertex);
//! Отключаем шейдерную программу
GL.UseProgram(0);
SwapBuffers();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
}
}
class Program
{
static void Main(string[] args)
{
var g = new Game(); //Создание окна
g.Run(30.0f);
}
}
}
Также нужно добавить System.Drawing:
Код хорошо описан в оригинальной статье, тут он делает тоже самое, структуру кода старался оставить такой же, так что проблем с пониманием быть не должно.
Результат работы программы:
Надеюсь заметка окажется полезной. Ошибки орфографии и оформления прошу слать в личку.
После установки на windows 10 visual studio 2015, в списке элементов панели управления нет элемента simpleOpenGLControl. Но элемента не будет в списке .NET Framework Components.
Проблема легко решается: в окне Choose Toolbox Items (Окно "выбор элементов панели управления" из урока по ссылке выше) на вкладке .NET Framework Components выбираем Browse и переходим в C:\Program Files (x86)\TaoFramework\bin. Выбираем Tao.Platform.Windows.dll
Затем фильтруем по слову simple (как на рисунке ниже) и выбираем нашедшийся компонент.
Сегодня мы будем работать с освещением. Это очень важная тема, умение работать с основами освещения пригодится Вам в большинстве Ваших собственных проектов.
Здесь может возникнуть вопрос зачем что-то менять, если по умолчанию в OpenGL уже выставлен источник света и мы и без того видим наши объекты.
Дело в том, что выставленный по умолчанию свет нельзя редактировать: изменять его цвет, насыщенность, положение и другие параметры. Но самое главное это то, что он всего один. Если Вам, к примеру, понадобится еще свет от ламп или фонарика, то стандартное освещение уже не поможет.
В прошлой статье мы создали, наверное, самое простое приложение, использующее мощь OpenGL, которое только может быть. Это всего лишь микроскопическая часть возможностей этой библиотеки. В этой главе я хочу поговорить с Вами о матрицах.
Как пишут во всех уроках и статьях на данную тему, скажу Вам, что, действительно, необходимости знать все свойства матриц у Вас нет, но их понимание способствовало бы скорейшему усвоению всех процессов OpenGL. С помощью матриц происходят смещение, поворот и прочие искажения пространства.
В OpenGL 1.0(именно эту версию мы пока используем) есть три типа матриц: GL_MODELVIEW, GL_PROJECTION и GL_TEXTURE. Остановимся на каждой из них немного подробнее.
Начнем с того, что сегодня самый лучший день для того, чтобы написать свое первое приложение для android. Если у Вас что-то не получалось до этого момента - будьте уверены, сегодня все получится!
И так. Вы установили новейшую версию Android Studio и все необходимые компоненты для работы с Android 4.4? Немного пощупали, изучили основные принципы работы в данной IDE и готовы покорять мир 3D графики? Отлично! Тогда приступим к работе.
В первую очередь создадим проект и назовем его OGLPart1.
Вы можете назвать его как угодно, но мне так будет удобнее. Уроков будет много и что бы не запутаться в них я буду нумеровать проекты эквивалентно статьям. Здесь все просто. Все, что от Вас требуется это указать имя проекта, папку для его хранения и минимальную поддерживаемую версию android. Стандартная процедура.
Это Ваш главный ресурс и помощник. Именно здесь лучше всего получать информацию о принципах работы команд и пояснения по функциям и их аргументам. Вас немного может приостановить тот факт, что сайт практически весь на английском языке, но это можно превратить в плюс. Если взять себя в руки и начать переводить неясные Вам вещи, то материал будет усвоен в разы лучше, ведь Вы хорошо поработаете для его изучения. Возможно, получится подучить язык, а это никогда не будет лишним.
В общем и целом, я хотел бы привить Вам большую любовь к изучению документаций. Не важно чем Вы решите заняться в будущем - официальная документация, практически в любом случае, будет полезнее тех статей, что Вы найдете в сети.
Доброго времени суток! Мое имя в сети Freaky Brainstorm и я android разработчик в одной из частных компаний. Начинающий, совсем еще «зеленый» разработчик, но все когда-то с этого начинали. Своими публикациями я хотел бы упростить жизнь некоторым людям, которые в связи со своей неопытностью, как и я столкнутся с проблемами, с которыми столкнулся я на пути android разработки. Приложение над которым я работаю в данный момент очень связанно с 3D графикой и, скорее всего, основной упор в блоге будет сделан на OpenGL. Я ни в коем случае, не утверждаю, что мои решения являются верными, и что поступать нужно именно таким образом который предлагаю я. Вовсе нет, здесь я просто описываю свое видение решения стоящих передо мной проблем программирования.
Если вы в первый раз открыли проект, скачав исходники с сайта и при попытки компиляции и запуска у вас вылетело исключение (щелкните на картинках для увеличения)
BadImageFormatException was unhandled
Была сделана попытка загрузить программу, имеющую неверный формат. (Исключение из HRESULT: 0x8007000B)
на строке Glut.glutInit(); (см рисунок выше), то вам необходимо в в поле Platform установить x86 вместо значения Any CPU:
Часто приходится вести отладочную информацию, чаще всего, в простых случаях это делается в консоль. Но гораздо чаще необходимо вести лог и в консоль, и в файл. Поведаю Вам о простом классике логирования, который симулирует стандартные cout, cerr, используя шаблонную функцию-оператор <<. Расскажу о некоторых специфичных моментах с которыми столкнулся.
Как известно Tao Framework, уже не развивается, и даже на его странице sourceforge, пишут: "возьмите OpenTK, как замену". Но вот появился луч надежды на возрождение, Tao Framework.
Всем привет. С момента, когда я написал свою последнюю статью прошло ровно девять месяцев. За это время я успел поступить в универ и узнать много нового в области программирования, с чем спешу поделиться. Хочу запустить цикл статей о том, как написать он-лайн игру средствами .NET. Подробно расскажу о клиент-серверной архитектуры приложения, о организации двухстороннего клиента без использования сервера, и многое многое другое.
Я сразу хотел бы поинтересоваться у вас, чего вы ждете от прочтения всех статей в цикле: чему бы хотели научиться, и о чем рассказать подробней. Свои мысли излагайте в комментариях к топику.
Цикл обещает быть объемным а над первой статьей я уже работаю в свободное от учебы время (таки сессия на носу). В конце, да и в процессе создания цикла будем писать две простеньких 2D игрушки, которые будут на своем примере показывать, как можно использовать протокол TCP\IP в разработке игр. Так что ждите=)
Товарищи, делаю все как в уроке, но вылетает черный экран!!! using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; // для работы с библиотекой OpenGL using Tao.OpenGl; // для работы с библиотекой FreeGLUT using Tao.FreeGlut; // для работы с элементом управления SimpleOpenGLControl using Tao.Platform.Windows;
namespace laba1 { public partial class Form1: Form { public Form1() { InitializeComponent(); AnT.InitializeContexts(); }
Выходят новые версии OpenGL. И все время, появляется информация, о том, что какие-то функции уже не рекомендуемые,а какие-то удалены. Фиксированный графический конвейер(ФГК) уходит, с новыми версиями OpenGL. Точнее он уже ушел, в OpenGL 3.3, core profile его уже нет. А, что же тогда приходит на смену традиционному, старому функционалу?!
Всем привет, имеется проблема, при наложении текстуры и изменения её размера она ложится плиткой, пытаюсь поставить TEXTURE_NO_TILE в настройках текстуры, однако это не к чему не приводит. Вот структура класса текстуры и меша
[Flags]
public enum Lib3dsTextureMapFlags
{
LIB3DS_TEXTURE_DECALE = 0x0001,
LIB3DS_TEXTURE_MIRROR = 0x0002,
LIB3DS_TEXTURE_NEGATE = 0x0008,
LIB3DS_TEXTURE_NO_TILE = 0x0010,
LIB3DS_TEXTURE_SUMMED_AREA = 0x0020,
LIB3DS_TEXTURE_ALPHA_SOURCE = 0x0040,
LIB3DS_TEXTURE_TINT = 0x0080,
LIB3DS_TEXTURE_IGNORE_ALPHA = 0x0100,
LIB3DS_TEXTURE_RGB_TINT = 0x0200
}
public class Lib3dsTextureMap
{
//public uint user_id;
//public object user_ptr;
public string name = "";
public Lib3dsTextureMapFlags flags;
public float percent;
public float blur;
public float[] scale = new float[2];
public float[] offset = new float[2];
public float rotation;
public float[] tint_1 = new float[3];
public float[] tint_2 = new float[3];
public float[] tint_r = new float[3];
public float[] tint_g = new float[3];
public float[] tint_b = new float[3];
}
public class Lib3dsMaterial
{
//public uint user_id;
//public object user_ptr;
public string name = ""; // Material name
public float[] ambient = new float[3]; // Material ambient reflectivity
public float[] diffuse = new float[3]; // Material diffuse reflectivity
public float[] specular = new float[3]; // Material specular reflectivity
public float shininess; // Material specular exponent
public float shin_strength;
public bool use_blur;
public float blur;
public float transparency;
public float falloff;
public bool is_additive;
public bool self_illum_flag;
public float self_illum;
public bool use_falloff;
public int shading;
public bool soften;
public bool face_map;
public bool two_sided; // Material visible from back
public bool map_decal;
public bool use_wire;
public bool use_wire_abs;
public float wire_size;
public Lib3dsTextureMap texture1_map = new Lib3dsTextureMap();
public Lib3dsTextureMap texture1_mask = new Lib3dsTextureMap();
public Lib3dsTextureMap texture2_map = new Lib3dsTextureMap();
public Lib3dsTextureMap texture2_mask = new Lib3dsTextureMap();
public Lib3dsTextureMap opacity_map = new Lib3dsTextureMap();
public Lib3dsTextureMap opacity_mask = new Lib3dsTextureMap();
public Lib3dsTextureMap bump_map = new Lib3dsTextureMap();
public Lib3dsTextureMap bump_mask = new Lib3dsTextureMap();
public Lib3dsTextureMap specular_map = new Lib3dsTextureMap();
public Lib3dsTextureMap specular_mask = new Lib3dsTextureMap();
public Lib3dsTextureMap shininess_map = new Lib3dsTextureMap();
public Lib3dsTextureMap shininess_mask = new Lib3dsTextureMap();
public Lib3dsTextureMap self_illum_map = new Lib3dsTextureMap();
public Lib3dsTextureMap self_illum_mask = new Lib3dsTextureMap();
public Lib3dsTextureMap reflection_map = new Lib3dsTextureMap();
public Lib3dsTextureMap reflection_mask = new Lib3dsTextureMap();
public uint autorefl_map_flags;
public int autorefl_map_anti_alias; // 0=None, 1=Low, 2=Medium, 3=High
public int autorefl_map_size;
public int autorefl_map_frame_step;
}
public class Lib3dsMesh
{
//public uint user_id;
//public object user_ptr;
public string name = ""; // Mesh name. Don't use more than 8 characters
public Lib3dsObjectFlags object_flags; // see Lib3dsObjectFlags
public byte color; // Index to editor palette [0..255]
public float[,] matrix = new float[4, 4]; // Transformation matrix for mesh data
public ushort nvertices; // Number of vertices in vertex array (max. 65535)
public List<Lib3dsVertex> vertices;
public List<Lib3dsTexturecoordinate> texcos;
public List<ushort> vflags;
public ushort nfaces; // Number of faces in face array (max. 65535)
public List<Lib3dsFace> faces;
public string box_front = "";
public string box_back = "";
public string box_left = "";
public string box_right = "";
public string box_top = "";
public string box_bottom = "";
public Lib3dsMapType map_type;
public float[] map_pos = new float[3];
public float[,] map_matrix = new float[4, 4];
public float map_scale;
public float[] map_tile = new float[2];
public float[] map_planar_size = new float[2];
public float map_cylinder_height;
}
Отрисовка текста в OpenGL, обычно нетривиальная задача, особенно для новичков. Стандартно эта задача сводится к рисованию прямоугольников с натянутой текстурой, на которой отображена та или иная буква. Располагая, которые, относительно друг, друга собираются в слова. Когда-то, когда я еще учился в Академии, мы делали на ASP.NET капчу отрисовывая строки текста и линии в картинку. На C# это делается очень легко, встроенными методами класса Graphics. Им и воспользуемся для рисования нашего текста. В этом посте я покажу очень примитивный и к сожалению не масштабируемый пример.=)
Всем привет, есть такая проблема, нужно загруженную часть 3Д модели отрисовать в JPG файле. В принципи частично у меня это получилось, однако отрисовывается только часть модели (скорее всего она не под нужным углом повёрнута, как развернуть её я хз). Или я гдето как обычно натупил. Подскажите где ошибка, или как это можно иначе реализовать?
Думаю практически все программисты(и не только) знают или хотя бы слышали, что такое freetype. Поэтому не будет долгих вступлении, копипаста информации с других ресурсов, о том, что такое freetype.
Turbulenz - игровой движок для социальных игр, использующий HTML 5, WebGL JavaScript. Его возможности позволяют делать захватающие реалтайм 3D игры, с физикой.
Стоит ли переходить на Windows 10?Windows 10 установлена на каждый 3-й компьютер. Какие плюсы от перехода? DirectX 12 работает только в Windows 10?
Как установить Windows 10?Как бесплатно и легально скачать? Как записать и установить с последними обновлениями?
Сохраните страницу!
Регистрация
Регистрируясь, вы принимаете правила сайта. Если вы не получили код подтв. регистрации - не
забудьте проверить папку спам.
×
Восстановление пароля
Пожалуйста, заполните поля, после чего вы получите код подтверждения на ваш Email. Если код не пришел в течении нескольких минут - проверьте папку спам.
×
Авторизация
Авторизуйтесь с помощью соц. сети или с помощью аккаунта на сайте: