Показаны сообщения с ярлыком OpenGL. Показать все сообщения
Показаны сообщения с ярлыком OpenGL. Показать все сообщения

четверг, 13 октября 2016 г.

Минимальный набор команд OpenGL для работы с вершинным шейдером

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

Glint programID = 0;
GLuint vaoID = 0;
char[] source = "the code1";//коды шейдеров
char[] source = "the code2";
Glint logLength = 0;
GLint compiled = 0;
GLint linked = 0;

Список команд в инициализирующей части:

programID = glCreateProgram();
glGenVertexArrays(1, &vaoID);
vs = glCreateShader(GLenum(GL_VERTEX_SHADER));
glShaderSource(vs, 1, &source, nil);
glCompileShader(vs);

//Работа с логами и кодами возврата компиляции шейдера
glGetShaderiv(vs, GLbitfield(GL_COMPILE_STATUS), &compiled);
glGetShaderiv(vs, GLenum(GL_INFO_LOG_LENGTH), &logLength);
glGetShaderInfoLog(vs, GLsizei(logLength), &logLength, cLog);
free(cLog);

fs = glCreateShader(GLenum(GL_FRAGMENT_SHADER));
glShaderSource(fs, 1, &fssptr, nil);
glCompileShader(fs);
//Здесь надо повторить обработку кода возврата компиляции и лог

glAttachShader(programID, vs);
glAttachShader(programID, fs);
glLinkProgram(programID);

//Работа с логами и кодами возврата линковки шейдера
glGetProgramiv(programID, UInt32(GL_LINK_STATUS), &linked);
glGetProgramiv(programID, UInt32(GL_INFO_LOG_LENGTH), &logLength);
glGetProgramInfoLog(programID, GLsizei(logLength), &logLength, cLog);

glDeleteShader(vs);
glDeleteShader(fs);

Список команд в рисующей части:
glClear(GLbitfield(GL_COLOR_BUFFER_BIT));
glUseProgram(programID);
glBindVertexArray(vaoID);

//glPointSize(40); и т.п.

glDrawArrays(GLenum(GL_POINTS), 0, 1);
        
glBindVertexArray(0);       
glFlush();

По завершении работы необходимо освободить ресурсы:

glDeleteVertexArrays(1, &vaoID);
glDeleteProgram(programID);

Это некая абстрактная схема, выраженная в виде инструкций на языке Си. На "человеческом" языке этот алгоритм можно описать так:

  1. Создаем пустую программу с идентификатором programID;
  2. Создаем массив вершин;
  3. Задаем исходники шейдера вершин;
  4. Компилируем их;
  5. Смотрим, есть ли ошибки компиляции, и обрабатываем их;
  6. Повторяем шаги 2-5 для других массивов;
  7. Привязываем все скомпиллированные шейдеры к программе programID;
  8. Компонуем ее;
  9. Смотрим, есть ли ошибки компоновки, и обрабатываем их;
  10. Вытираем шейдеры; Все готово для команд рисования;
  11. Указываем, что будем пользоваться программой с индентификатором programID; Я так понял, что таких "программ" может быть несколько в одном приложении и мы можем переключаться между ними;
  12. "Привязываем" массив вершин;
  13. glDrawArrays - рисуем;
  14. "Отвязываем" массив вершин;
  15. При выходе из приложения явно вытираем массивы вершин и "программы";

суббота, 11 июля 2015 г.

Сцены OpenGL в Mac OS X

Ну раз говорится про Mac OS, значит программировать будем в cocoa на языке Objective-C. Cоздадим простейшее приложение OpenGL, рисующее на "сцене" куб. Откроем среду разработки XCode и в меню выберем создать новый проект. В списке предложений находим Cocoa Application. Жмем Next.
Выбираем именем рашего проекта CocoaOpenGL и языком разработки объектный си. Далее произвольно заполняем остальные пустые поля. Жмем Next.

Видим "пустой" каркас нашего будущего приложения: 

Во вкладке Build Settings находим поле Linked Frameworks and Libraries и в появившемся списке выбираем OpenGL.framework . Фреймворк, как вы догадались, это заголовочные файлы и бинарные библиотеки для OpenGL .
 Далее создаем пустые заголовочный и реализующий класс Сube файлы из меню "создать новый файл". Класс Cube и является классом, реализующим OpenGL сцену.
Задаем имя CocoaOpenGL.h . Жмем Next:
Это мы создали заголовочный файл. Теперь аналогично создаем файл реализации(эти оба файла пока пусты):
Жмем Next:
 Пишем в поле имени CocoaOpenGL . Жмем Next:
 И, наконец, create, мы увидим пустой файл реализации:
 Новый заголовочный файл заполняем таким содержимым:
@interface Cube : NSOpenGLView
{
    bool        initialized;
    float        rotation[3];
}
+ (NSOpenGLPixelFormat*) basicPixelFormat;
- (void) drawRect: (NSRect) bounds;
- (void) resizeGL:(NSRect)rect;
- (id)initWithFrame:(NSRect)frameRect;
@end
Файл реализации делаем пока таким:
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import "CocoaOpenGL.h"
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
@implementation Cube
+ (NSOpenGLPixelFormat*) basicPixelFormat{
}
- (void) resizeGL:(NSRect)rect{
}
- (id)initWithFrame:(NSRect)frameRect{
}
- (void)drawRect:(NSRect)rect {
[super drawRect:rect];
// Здесь будет рисующий код
}
@end
Далее заполняем пустые тела функций следующим содержанием. basicPixelFormat :
    static NSOpenGLPixelFormatAttribute    attributes[] =   
    {
        //NSOpenGLPFAWindow,
        NSOpenGLPFADoubleBuffer,
        NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)32,
        (NSOpenGLPixelFormatAttribute)nil
    };
    return ([[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease]);
initWithFrame :
     NSOpenGLPixelFormat    *pf;
     pf   = [Cube basicPixelFormat];
     self = [super initWithFrame: frameRect pixelFormat: pf];  
     return (self);
drawRect :
    int            width, height;
    int            i, j;       
    float         aspectRatio;

    if (!initialized)
    {
        rotation[0] = 45.0f;
        rotation[1] = 45.0f;
        rotation[2] = 45.0f;
        initialized = true;
    }

    width  = rect.size.width;
    height = rect.size.height;
 
    glViewport(0, 0, width, height);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    aspectRatio = (GLfloat)width/(GLfloat)height;
    //  gluPerspective(100.0f, aspectRatio, 1, 2);
    if(width <= height)
    {
        glOrtho(-2.0f, 2.0f, -2/aspectRatio, 2/aspectRatio, 2.0f, -2.0f);
    }
    else
    {
        glOrtho(-2*aspectRatio, 2*aspectRatio, -2.0f, 2.0f, 2.0f, -2.0f);
    }
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRotatef(rotation[0], 1.0f, 0.0f, 0.0f);
    glRotatef(rotation[1], 0.0f, 1.0f, 0.0f);
    glRotatef(rotation[2], 0.0f, 0.0f, 1.0f);
    // рисуем куб
    glEnable(GL_DEPTH_TEST); 
    glBegin(GL_QUADS);
    for (i = 0; i < 6; i ++)
    {
        glColor3fv(colors[i]);
        for (j = 0; j < 4; j ++)
            glVertex3fv(corners[sides[i][j]]);
    }
    glEnd();  
  
    glFlush();
    //[[self openGLContext]flushBuffer];

Обратите внимание, что функция resizeGL - пуста. Код, который реагирует на изменение собственно сцены (не так в Линуксе и я Windows), находится теле основной рисующей функции drawRect. 
Также после директив импорта в файле реализации вставляем такие строки:
static const GLfloat    corners[8][3] =   
{
    {  1.0f,  1.0f,  1.0f },    // Front top right
    {  1.0f, -1.0f,  1.0f },    // Front bottom right
    { -1.0f, -1.0f,  1.0f },    // Front bottom left
    { -1.0f,  1.0f,  1.0f },    // Front top left
    {  1.0f,  1.0f, -1.0f },    // Back top right
    {  1.0f, -1.0f, -1.0f },    // Back bottom right
    { -1.0f, -1.0f, -1.0f },    // Back bottom left
    { -1.0f,  1.0f, -1.0f }     // Back top left
};
static const int    sides[6][4] =   
{
    { 0, 1, 2, 3 },                // Front
    { 4, 5, 6, 7 },                // Back
    { 0, 1, 5, 4 },                // Right
    { 2, 3, 7, 6 },                // Left
    { 0, 3, 7, 4 },                // Top
    { 1, 2, 6, 5 }                 // Bottom
};
static const GLfloat    colors[6][3] = 
{
    { 1.0f, 0.0f, 0.0f },        // Red
    { 0.0f, 1.0f, 0.0f },        // Green
    { 1.0f, 1.0f, 0.0f },        // Yellow
    { 0.0f, 0.0f, 1.0f },        // Blue
    { 1.0f, 0.0f, 1.0f },        // Magenta
    { 0.0f, 1.0f, 1.0f }         // Cyan
};
Далее идем в Main.storyboard:
 Добавляем OpenGL View c хранилища элементов управления главный вид приложения. В правом верхнем углу среды разработки в поле Custom Class указываем на класс Cube (при выделенном элементе управления OpenGL View)
 Далее на вкладке Build Settings поле Objective-C Automatic Reference Counting в значение No
 Для нашего OpenGL View элемента надо установить поле Depth в любое не-none значение:
 Компилируем, запускаем и видим:

суббота, 10 декабря 2011 г.

Некоторые базовые понятия OpenGL


Область (объем) отсечения определяется минимальным и максимальным значениями пространственных (логических декартовых) координат, которые видно в окне.
Поле просмотра - это область внутри клиента окна, которое используется для рисования области отсечения. Измеряется в пикселах или реальных экранных координатах. Поле просмотра обычно эквивалентно области окна, но не всегда. С помощью поля просмотра можно сжимать или растягивать изображения внутри окна, а также отоьражать только часть области отсечения (в этом случае поле просмотра задается больше чем область клиента окна).
Есть два типа отображения трехмерной области отсечения на поле просмотра: это ортографическая и перспективная проекции.
glViewport - используется для задания поля просмотра в окне.
glOrtho - устанавливает границы объема отсечения.
Между заданием вершин и появлением их на экране происходит три типа преобразований:
1)наблюдения;
2)модели;
3)проецирования;
Терминология преобразований OpenGL
1)преобразование наблюдения - задает положение камеры;
2)преобразование модели - перемещает объекты по сцене;
3)преобразование наблюдения модели - описывает "дуализм" преобразований наблюдения и ммодели;
4)преобразование проецирования - задает наблюдаемый объем;
5)преобразование поля просмотра - псевдопреобразование, масштабирующее конечный результат согласно размерам окна.
Координаты наблюдения отсчитываются от точки, в которой расположен глаз наблюдателя (эти координаты можно рассматривать как абсолютные экранные координаты). Все преобразования описываются с точки зрения их эффекта в системе координат наблюдения. При отсутствии преобразований система координат наблюдения такова, что камера находится в начале координат и смотрит по направлению отрицательных z. Верх камеры ориентирован в этом случае в сторону положительных у.
Проекция модели (modelview) означает, что это преобразование можно считать либо преобразованием модели либо преобразованием проецирования. Разницы между ними для конечного результата нет.
Тоже касается преобразования наблюдения модели.
Исходное преобразование дает точку отсчета для других преобразований.
Конвейер преобразований вершины
1)исходные данные по вершине:
2)матрица наблюдения модели;
3)преобразованные координаты наблюдения;
4)матрица проекции;
5)координаты отсечения;
6)перспективное деление и преобразование поля просмотра;
7)координаты окна;
glFrustum - трехмерный аналог gluOrtho2D. Создает матрицу перспективной проекции и умножает ее на текущую. Наблюдатель расположен в (0,0,0) и смотрит в отрицательном направлении z.
gluLookAt - создает видовую матрицу. Определяет преобразование наблюдения на основе положения наблюдателя, центра сцены и вектора, указывающего "вверх" от наблюдателя.
gluPerspective - создает матрицу наблюдения перспективной проекции, описывающую наблюдаемый объем (усеченная пирамида) в глобальных координатах. Аспект должен соответствовать характеристическому отношению поля просмотра (задается glViewPort)

пятница, 9 декабря 2011 г.

Кватернион и матрица вращения OpenGL

Кваренион - это такая четырехкомпонентная сущность, с помощью которой можно задавать вращения в трехмерном пространстве. Их преимущество перед матрицами, соответствующим этим поворотам, состоит в том, что для них, кватернионов, нет так называемой "шарнирной блокировки" и что вычисления кватернионной математики менее ресурсоемки, чем для соответствующих матричных операций. Но в OpenGL пока еще манипулируют поворотами в пространстве с помощью матриц. Посмотрим, как соотносятся кватернион и соответствующая ему матрица, задающая поворот.
Кватернион представляется в виде черырех чисел x, y, z, w:

где:
угол альфа выражается в радианах, а е - это единичный вектор, вокруг которого совершается вращение.
Вектор v переводится вращением в вектор v' с помощью кватерниона так:

Матрица поворота выражается через компоненты кватерниона следующим способом:

Не забываем, что в OpenGL матрицы разворачиваются по столбцам, а не построкам. Таким образом, если матрицу обозначаем через matrixOfRotation, то, используя синтаксис языка си мы ее элементы запишем следующим образом:


matrixOfRotation[0] = 1.0f - 2.0f*( y*y + z*z );
matrixOfRotation[1] = 2.0f * ( x*y + z*w);
matrixOfRotation[2] = 2.0f * ( x*z - y*w);
matrixOfRotation[3] = 0.0f;
matrixOfRotation[4] = 2.0f * ( x*y - z*w );
matrixOfRotation[5] = 1.0f - 2.0f*( x*x + z*z );
matrixOfRotation[6] = 2.0f * ( z*y + x*w );
matrixOfRotation[7] = 0.0f;
matrixOfRotation[8] = 2.0f * ( x*z + y*w );
matrixOfRotation[9] = 2.0f * ( y*z - x*w );
matrixOfRotation[10] = 1.0f - 2.0f*( x*x + y*y );
matrixOfRotation[11] = 0.0f;
matrixOfRotation[12] = 0;
matrixOfRotation[13] = 0;
matrixOfRotation[14] = 0;
matrixOfRotation[15] = 1.0f;


воскресенье, 21 ноября 2010 г.

Сцены OpenGL в виджетах GTK

Весь интернет пестрит примерами использования OpenGL. Найти их не сложно. OpenGL сам по себе является кроссплатформенным. А поэтому, чтобы создать сцену OpenGL вам нужно знать только сам OpenGL. А вот когда сама сцена создана, встает вопрос - и куда мы эту сцену будем "запихивать"? Вот тут-то и всплывает "однобокость" этого великого разнообразия примеров OpenGL: они демонстрируют создание упомянутых сцен в одном единственном окне, окне библиотеки GLUT. Эта библиотека, как я понял, и была создана всего-навсего для написания "образовательных" и демонстрационных целей. И эти цели она выполнила и даже перевыполнила. Также к ее плюсам можно добавить ее кроссплатформенность. Ее реализиция есть и для систем UNIX и для Windows. Но ведь сцены OpenGL не являются необходимыми "сами по себе". Понятно же, что приложение, которое представляет из себя одно единственное окно с ,пусть с даже очень впечатляющим и красивым, двух- или трех- мерным изображением, вряд ли представит из себя какой-либо интерес, кроме, как мы уже указали, демонстрационного.
Здесь мы рассмотрим как вставить сцену OpenGL в виджет GTK. Интерфейс любого приложения GTK является набором, комбинацией так-называемых виджетов, размещенных, как укажет программист, на окнах приложения. Четкого определения, что такое виджет нет, из-за того, что это очень общее, объемное, понятие. Причем его "общность" истинна, так как на самом деле понять, что такое виджет совсем просто. Сложнее рассказать. Виджет - это "нечто", что можно отобразить на эране, окне приложения, диалоге. Любая кнопка, элемент управления, некая область, которая четко отделена и распознаваема от остальной части дилога, окна и есть виджет. Виджет GTK- это просто некий тип, структура GtkWidget.
Вот мы и подобрались вплотную к основной теме. Рассмотрим два виджета, которые позволяют рисовать сцены OpenGL в себя. Эти виджеты: gtkglarea и gtkglext. Имеются их реализации в виде пакетов для. самых распространенных по-крайней мере, дистрибутивов Linux. Также есть их порты для FreeBSD, что заставляет меня предполагать, что они есть для всех систем BSD.
Рассмотрим сначала gtkglarea.
Создаем файл gtkglarea_demo.c следующего содержания (вообще-то это совсем немного измененный для собственных нужд файл взятый отсюда):


#include <GL/gl.h>
#include <GL/glu.h>
#include <gtk/gtk.h>
#include <gtkgl/gtkglarea.h>

gint glarea_button_press (GtkWidget*, GdkEventButton*);
gint glarea_draw (GtkWidget*, GdkEventExpose*);
gint glarea_reshape (GtkWidget*, GdkEventConfigure*);
gint glarea_init (GtkWidget*);
gint glarea_destroy (GtkWidget*);
int main (int, char**);

gint
glarea_button_press (GtkWidget* widget, GdkEventButton* event) {
int x = event->x;
int y = event->y;
if (event->button == 1) {
g_print ("Button 1 press (%d, %d)\n", x, y);
return TRUE;
}
if (event->button == 2) {
g_print ("Button 1 press (%d, %d)\n", x, y);
return TRUE;
}
return FALSE;
}

gint
glarea_draw (GtkWidget* widget, GdkEventExpose* event) {
if (event->count > 0) {
return(TRUE);
}
g_print ("Expose Event\n");
if (gtk_gl_area_make_current(GTK_GL_AREA(widget))) {
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* */
/* Insert your drawing code here. */
/* */
gtk_gl_area_swapbuffers (GTK_GL_AREA(widget));
}
return (TRUE);
}

gint
glarea_reshape (GtkWidget* widget, GdkEventConfigure* event) {
int w = widget->allocation.width;
int h = widget->allocation.height;
g_print ("Reshape Event\n");
if (gtk_gl_area_make_current (GTK_GL_AREA(widget))) {
/* This is an example 2D reshape function. Writing reshape */
/* functions is beyond the scope of this demo. Check the */
/* red book or the www.opengl.org for more information on */
/* how to write reshape code to suit your needs. */
glViewport (0, 0, w, h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluOrtho2D (-(w>>1), (w>>1), -(h>>1), h>>1);
glMatrixMode (GL_MODELVIEW);
}
return (TRUE);
}

gint
glarea_init (GtkWidget* widget) {
g_print ("Realize Event\n");
if (gtk_gl_area_make_current (GTK_GL_AREA(widget))) {
/* Insert your OpenGL initialization code here */
}
return TRUE;
}

gint
glarea_destroy (GtkWidget* widget) {
g_print ("GTK GL Area Destroy Event\n");
/* Insert any required cleanup */
/* code here. */
return TRUE;
}

int
main (int argc, char** argv) {
GtkWidget* window;
GtkWidget* button_quit;
GtkWidget* box_main;
GtkWidget* glarea;
/* These attributes are passed to glXChooseVisual by the */
/* gdk (see gdk_gl_choose_visual in gdkgl.c from the GtkGlarea distro). */
int attrlist[] = {
GDK_GL_RGBA,
GDK_GL_DOUBLEBUFFER,
GDK_GL_DEPTH_SIZE, 1,
GDK_GL_NONE
};
gtk_init (&argc, &argv);
if(gdk_gl_query() == FALSE) {
g_print("OpenGL not supported!\n");
return (1);
}

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW(window), "GtkGLArea Demo For Blogger");
gtk_quit_add_destroy (1, GTK_OBJECT(window));
gtk_signal_connect (GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
gtk_container_set_border_width (GTK_CONTAINER(window), 10);
gtk_widget_show (window);

box_main = gtk_vbox_new (FALSE, 10);
gtk_container_add (GTK_CONTAINER(window), box_main);
gtk_widget_show (box_main);

glarea = gtk_gl_area_new(attrlist);
gtk_widget_set_events(GTK_WIDGET(glarea),
GDK_EXPOSURE_MASK|
GDK_BUTTON_PRESS_MASK);
gtk_signal_connect (GTK_OBJECT(glarea), "button_press_event", GTK_SIGNAL_FUNC(glarea_button_press), NULL);
gtk_signal_connect (GTK_OBJECT(glarea), "expose_event", GTK_SIGNAL_FUNC(glarea_draw), NULL);
gtk_signal_connect (GTK_OBJECT(glarea), "configure_event", GTK_SIGNAL_FUNC(glarea_reshape), NULL);
gtk_signal_connect (GTK_OBJECT(glarea), "realize", GTK_SIGNAL_FUNC(glarea_init), NULL);
gtk_signal_connect (GTK_OBJECT(glarea), "destroy", GTK_SIGNAL_FUNC (glarea_destroy), NULL);
gtk_widget_set_usize(GTK_WIDGET(glarea), 256, 256);
gtk_box_pack_start (GTK_BOX(box_main), glarea, FALSE, TRUE, 0);
gtk_widget_show (glarea);

button_quit = gtk_button_new_with_label ("Quit");
gtk_signal_connect (GTK_OBJECT(button_quit), "clicked", GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
gtk_box_pack_start (GTK_BOX(box_main), button_quit, FALSE, TRUE, 0);
gtk_widget_show (button_quit);

gtk_main ();

return (0);
}


И компиллируем его следующей командой:
#gcc gtkglarea_demo.c -o gtkglarea_demo `pkg-config gtk+-2.0 glib-2.0 gtkgl-2.0 --cflags --libs`
Запускаем на выполнение gtkglarea_demo и видим диалоговое окно с черным квадратом, что и есть (пустая) сцена OpenGL и кнопкой выхода под нашим виджетом.
Файл gtkglarea.h содержится в пакете libgtkgl2.0-dev.
Рассмотрим теперь "конкурента", то есть gtkglext.
Аналогично, создаем файл gtkglext_demo.c с содержимым (он является упрощенной и измененной версией файла simple.c из пакета libgtkglext1-dev):


#include <stdlib.h>
#include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#include <GL/gl.h>
#include <GL/glu.h>

static void
realize (GtkWidget *widget, gpointer data){
GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);

if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
return;

glClearColor (0.0, 0.0, 0.0, 0.0);
glClearDepth (1.0);
glViewport (0, 0, widget->allocation.width, widget->allocation.height);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective (40.0, 1.0, 1.0, 10.0);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
gluLookAt ( 0.0, 0.0, 3.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0);
glTranslatef (0.0, 0.0, -3.0);

gdk_gl_drawable_gl_end (gldrawable);
}

static gboolean
configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer data){
GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);

if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
return FALSE;
glViewport (0, 0, widget->allocation.width, widget->allocation.height);
gdk_gl_drawable_gl_end (gldrawable);

return TRUE;
}

static gboolean
expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data){
GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);

if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
return FALSE;
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (gdk_gl_drawable_is_double_buffered (gldrawable))
gdk_gl_drawable_swap_buffers (gldrawable);
else
glFlush ();
gdk_gl_drawable_gl_end (gldrawable);

return TRUE;
}

int
main (int argc, char *argv[]){
GdkGLConfig *glconfig;
gint major, minor;
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *drawing_area;
GtkWidget *button;

gtk_init (&argc, &argv);
gtk_gl_init (&argc, &argv);
gdk_gl_query_version (&major, &minor);
g_print ("\nOpenGL extension version - %d.%d\n",major, minor);

glconfig = gdk_gl_config_new_by_mode ( GDK_GL_MODE_RGB |
GDK_GL_MODE_DEPTH |
GDK_GL_MODE_DOUBLE);
if (glconfig == NULL){
glconfig = gdk_gl_config_new_by_mode ( GDK_GL_MODE_RGB |
GDK_GL_MODE_DEPTH);
if (glconfig == NULL)
exit (1);
}

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "GtkGLExt Demo For Blogger");
gtk_container_set_reallocate_redraws (GTK_CONTAINER (window), TRUE);
g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
gtk_container_set_border_width (GTK_CONTAINER(window), 10);
gtk_widget_show (window);

vbox = gtk_vbox_new (FALSE, 10);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show (vbox);

drawing_area = gtk_drawing_area_new ();
gtk_widget_set_size_request (drawing_area, 256, 256);
gtk_widget_set_gl_capability ( drawing_area,
glconfig,
NULL,
TRUE,
GDK_GL_RGBA_TYPE);
g_signal_connect_after (G_OBJECT (drawing_area), "realize", G_CALLBACK (realize), NULL);
g_signal_connect (G_OBJECT (drawing_area), "configure_event", G_CALLBACK (configure_event), NULL);
g_signal_connect (G_OBJECT (drawing_area), "expose_event", G_CALLBACK (expose_event), NULL);
gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
gtk_widget_show (drawing_area);

button = gtk_button_new_with_label ("Quit");
g_signal_connect (G_OBJECT (button), "clicked",G_CALLBACK (gtk_main_quit), NULL);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);

gtk_main ();

return 0;
}


Компиллируем
#gcc gtkglext_demo.c -o gtkglext_demo `pkg-config gtk+-2.0 glib-2.0 gtkglext-1.0 --cflags --libs`
И получаем исполняемый файл gtkglext_demo, который дает тот же результат, что и предыдущий пример, только заголовок окна приложения соответственно изменен.
Коротко обсудим разницу между этими двумя протыми приложениями.
Интерфейс gtkglarea можно посмотреть в файле gtkglarea.h из пакета libgtkgl2.0-dev.
Там можно увидеть, что этот виджет является прямым потомком виджета GtkDrawingArea. Еще одним полем в структуре, соответствующей этому виджету, есть переменная типа GdkGLContext. И в первом примере мы с этим виджетом обращаемся стандартно: создаем его своим собственным конструктором, пакуем, устанавливаем сигналы, показываем. Когда рисуем в этот виджет, то пользуемся функцией gtk_gl_area_make_current.
В случае с gtkglext работа происходит несколько иначе. Создается объект типа GdkGLConfig и потом он "привязывается" к виджету GtkDrawingArea. Затем, когда посылаем команды OpenGL в этот виджет, то создаем объекты типа GdkGLContext и GdkGLDrawable, после чего заключаем команды OpenGL в "функциональные скобки" gdk_gl_drawable_gl_begin и gdk_gl_drawable_gl_end.
Оба примера были протестированы на squeeze Debian.

вторник, 24 августа 2010 г.

Минимальный набор функций OpenGL для работы с текстурами

1)
a)Первым необходимым шагом при наложении карты текстуры на геометрический объект является загрузка текстуры в память. Загруженная текстура становиться частью текущего "состояния текстуры". Для этой загрузки используются функции
void glTexImage{N}D(...);
Текстура грузится из памяти, на которую указывает указатель, являющийся последним аргументом этих функций.Или одно- и двух- мерные текстуры можно загрузить также из буфера цвета:
void glCopyTexImage{1,2}D(...);
К загруженным данным текстуры применяются упаковка пикселей, масштабирование пикселей таблицы цветов, свертки и т.д.
b)Чтобы пользоваться этими функциями необходимо включить или выключить данное состояние текстуры:
glDisable(GL_TEXTURE_{M}D);
glEnable(GL_TEXTURE_{N}D);

2)То, как OpenGL объединяет цвета текселей с цветом геометрического объекта, на который накладывается текстура, зависит от режима текстурной среды, который устанавливается функцией glTexEnv.Правила визуализации и поведение применяющихся карт текстуры можно также изменением параметров функций семейства glTexParameter.
3)Далее надо задать текстурные координаты вершин (То есть присвоение вершинам объекта, на который накладываем текстуру, "координат" в "пространстве" текстуры). Обращение к текселям карты текстуры выполняется не как к ячейкам памяти (как для пиксельных образов), а как к абстрактным текстурным координатам (переменные с плавающей запятой в диапазоне от 0 до 1):
void glTexCoord{N}f(...);
Эта функция вызывается между glBegin и glEnd, перед соответствующей функцией glVertex{N}x(...);Для работы с несколькими текстурами и более быстрого переключения между ними используются функции:
a)glGenTextures - принимает в качестве параметров указатель на массив целых чисел и его размер. Эти целые числа играют роль идентификаторов текстур (текстурных объектов).
b)glBindTexture - делает текущим выбранный текстурный объект.
c)glDeleteTextures - освобождает ресурсы, занятые текстурными объектами.

четверг, 3 декабря 2009 г.

Минимальный набор функций OpenGL для освещения сцены

Освещение сцены OpenGL необходимо не только для ее собственно освещения, но без реализации освещения сцена не будет восприниматься как трехмерная. Приведем минимальный набор функций для релизации этой возможности:
1)По умолчанию машина OpenGL находиться в состоянии с выключенным освещением. Включим его:
glEnable(GL_LIGHTING);
2)Но это только "состояние" этой машины. Нужно теперь "включить" источник света. Он может быть не один. На каждый - своя команда:
glEnable(GL_LIGHT0);
3)Кроме просто включить, надо еще установить свойства каждого источника света:
glLightfv(GL_LIGHT0, GL_POSITION, l0_pos);
glLightfv(GL_LIGHT0, GL_DIFFUSE , l0_dif);
Первый параметр указывает источник, второй - свойство, третий - значение свойства. Этих двух параметров достаточно для "минимальной" функциональности.
А вообще параметров целое множество:
а)Параметры GL_SPOT_EXPONENT,GL_SPOT_CUTOFF,GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION и GL_QUADRATIC_ATTENUATION устанавливаются функциями glLightf() и glLighti().
б)Параметры GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR,GL_POSITION, GL_SPOT_CUTOFF, GL_SPOT_DIRECTION, GL_SPOT_EXPONENT,GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION и GL_QUADRATIC_ATTENUATION утанавливаются функциями glLightfv() или glLightiv()
4)Свойства есть не только у света, но и у материала, который освещаеться:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, par_mat);Первый параметр функции определяет сторону материала, для которой задаются свойства, второй задает свойство, третий - его значение.