PSPinfo.RU > HomeBrew > Уроки программирования. Урок четвёртый!

Уроки программирования. Урок четвёртый!


10.10.09. Разместил: .:Witcher:.

alt


В данном уроке мы научимся рисовать простые и 3d обьёкты средствами OpenGL на экране PSP.

Найти прошлые уроки вы можете в поиске в строке указать программирование


У вас уже должен стоять SDK, как это сделать описано тут

Подготовка
Создаём на рабочем столе или в любом удобном вам месте папку 'lessonsPSP'. Там создаём такие файлы с содержанием:

Файл compile.bat
make clean
make
pause


Файл Makefile:
TARGET = Lessons4
OBJS = main.o
PSPPATH = $(shell psp-config --psp-prefix)
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
BUILD_PRX = 1
PSP_FW_VERSION=371
PSP_LARGE_MEMORY = 1
INCDIR = $(PSPPATH)/include/bolbol
LIBS = -lglut -lGLU -lGL -lpspvfpu -lpspge -lpspsdk -lpspctrl -lpsprtc -lpsppower -lpspgum -lpspgu -lm -lstdc++
LDFLAGS += -DMODULE_NAME="OpenGL" psp-setup.cpp
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = TEST OPENGL
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak


Файл psp-setup.cpp
#include
#include
#if !defined(MODULE_NAME)
#define MODULE_NAME NONAME
#endif
#if !defined(MODULE_VERSION_MAJOR)
#define MODULE_VERSION_MAJOR 1
#endif
#if !defined(MODULE_VERSION_MINOR)
#define MODULE_VERSION_MINOR 0
#endif
#if !defined(MODULE_ATTR)
#define MODULE_ATTR 0
#endif
#define _stringify(s) _tostring(s)
#define _tostring(s) #s
PSP_MODULE_INFO(_stringify(MODULE_NAME), 0, 1, 0);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
PSP_HEAP_SIZE_KB(12*1024);
static
int exit_callback (int arg1, int arg2, void *common)
{
sceKernelExitGame();
return 0;
}
static
int update_thread (SceSize args, void *argp)
{
int cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}
static void setup_callbacks (void) _attribute_(constructor);
static void setup_callbacks (void)
{
int id;
if (id = sceKernelCreateThread("update_thread", update_thread, 0x11, 0xFA0, 0, 0) >= 0)
sceKernelStartThread(id, 0, 0);
}
static void back_to_kernel (void) _attribute_(destructor);
static void back_to_kernel (void)
{
sceKernelExitGame();
}


Подробно разбирать их работу не будем, и не надо, ЭТО БУДЕТ НАШ ШАБЛОН.
Первый файл при запуске его компилирует проэкт
Второй это настройка компиляции
Третий это стандартные функции для PSP, их трогать не надо, мы вынесли их в отдельный файл что бы не писать каждый раз в новый проэкт.

Эти файлы можно кинуть в отдельную папку, они нам будут нужны для каждого нового проэкта, и их редактировать не надо (кроме файла Makefile, но его пока что тоже, все нужные библиотеки я вынес)

Создаём наш рабочий файлик main.cpp
Его содержание:
#include //нужна для выхода
#include //математическая функция
#include //Подключаем нужные библиотеки для работы с OpenGL
#include //Библиотека GLUT
#include //Библиотека GLU, которую мы будем использовать позже.

//функция где мы указываем что выводит
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT); //закрашиваем цвет
//начало рисование обьекта
glBegin(GL_LINES); //рисуем примитив- линию
glColor3f(1,0,0); //цвет линии- черный
glVertex2f(-1,0); //первая вершина
glVertex2f(1,1); //вторая вершина
glEnd(); //заканчиваем рисовать примитив
//конец
glutSwapBuffers();
glFlush(); //выводим изображение на экран
}
int main(int argc, char **argv)
{
glutInit(&argc,argv); //инициализация
glutInitDisplayMode(GLUT_DOUBLE); //установка режима отображения
glutInitWindowSize(480, 272); //размер окна
glutInitWindowPosition(0,0); //позиция окна
glutCreateWindow("lessons4"); //заглавие нашего окошка
glClearColor(1,1,1,1); //заполняем белым цветом
glutDisplayFunc(Display); //функция для рисования(она у нас выше)
glutMainLoop(); //Обработчик событий
return 1; //возращаем 1(пока что не важно)
}


Подробней описание функции Display чуть ниже.
Режимы (функция glutDisplayFunc)
GLUT_RGB — режим RGB (отображение цвета с помощью трех основных цветов — красного, зеленого и синего). Используется по умолчанию.

GLUT_SINGLE — окно с одиночным буфером. Этот режим используется также по умолчанию.

GLUT_DOUBLE — окно с двойным буфером (см. выше).

GLUT_DEPTH — окно с буфером глубины (Z-buffer’ом), о котором тоже уже сказано.

Запускаем наш батник, происходит компиляция. После кидаем файл EBOOT.PBP в папку X:/PSP/GAME/less4/ где X буква вашей psp

И запускаем!
Ура! Мы нарисовали линию)
Но почему она на весь экран по оси Х растянулась??
Дело в том что наше поле с объектом масштабировать до размера самого объекта.
Давайте дополним наш код, и добавим ему обработчик кнопок.
Для этого после:
glutDisplayFunc(Display);

Вставляем:

glutKeyboardFunc(keyPressed);
glutKeyboardUpFunc(keyReleased);
glutReshapeFunc(DisplayResize);

Что это делает?
Первая команда обращается к функции keyPressed в момент нажатия любой клавиши
Команда glutKeyboardUpFunc по сути делает тоже самое, но обращается к функции
keyReleased ПОСЛЕ нажатия любой клавиши.
Последняя команда самая интересная - именно она будет задавать сетку и вычислять соотношения геометрических размеров для окна.
Вот сами наши функции:


GLvoid DisplayResize(GLsizei Width, GLsizei Height)
{
if (Height==0) // Предотвращение деления на ноль, если окно слишком мало
Height=1;

glViewport(0, 0, Width, Height); // Сброс текущей области вывода
// и перспективных преобразований

glMatrixMode(GL_PROJECTION); // Выбор матрицы проекций
glLoadIdentity(); // Сброс матрицы проекции

gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
// Вычисление соотношения геометрических размеров для окна
glMatrixMode(GL_MODELVIEW); // Выбор матрицы просмотра модели
}

/* обработчик нажатия кнопки. */
void keyPressed(unsigned char key, int x, int y)
{
switch (key) {
case 'd': /* delta, triangle */
break;
case 'o': /* round */
break;
case 'q': /* square*/
break;
case 'x': /* cross */
break;
case 'n': /* key with the music note */
break;
case 's': /* select */
break;
case 'a': /* startbutton */ /* If START is pressed,
kill everything. */
/* exit the program...normal termination. */
exit(0);
default:
;
}
}

/* обработчик на отпускание кнопки. */
void keyReleased(unsigned char key, int x, int y)
{
switch (key) {
case 'd': /* delta, triangle */
break;
case 'o': /* round */
break;
case 'q': /* square*/
break;
case 'x': /* cross */
break;
case 'n': /* key with the music note */
break;
case 's': /* select */
break;
case 'a': /* startbutton */
break;
default:
;
}
}


Внимание!
Стоить учесть что функцию вывода glFlush() можно убрать.

Эти функции можно подробно и не разбирать, вроде всё понятно.

Давайте разберём нашу функцию Display
Первая строка думаю понятна. Далее идёт такой синтаксис:
glBegin(тип);
glVertex*(…);
....
glVertex*(…);
glEnd();


Где * может быть [2,3][i,d,f,b], в зависимости от количества точек и от типа чисел, которые мы будет использовать при задании точки. Например, если мы будем рисовать в двухмерном пространстве и при этом использовать тип double, тогда функция будет иметь вид glVertex2d(x,y). Я думаю, с этим все ясно.

Список типов:
•GL_POINTS — каждая вершина задает координаты некоторой точки.

•GL_LINES — каждая отдельная пара вершин определяет отрезок; если задано нечетное число вершин, то последняя вершина игнорируется.

•GL_LINE_STRIP — каждая следующая вершина задает отрезок вместе с предыдущей.

•GL_LINE_LOOP — отличие от предыдущего примитива только в том, что последний отрезок определяется последней и первой вершиной, образуя замкнутую ломаную.

•GL_TRIANGLES
— каждая отдельная тройка вершин определяет треугольник; если задано некратное трем число вершин, то остаток игнорируется.

•GL_TRIANGLE_STRIP — каждая следующая вершина задает треугольник вместе с двумя предыдущими.

•GL_TRIANGLE_FAN — треугольники задаются первой и каждой следующей парой вершин (пары не пересекаются).

•GL_QUADS — каждая отдельная четверка вершин определяет четырехугольник; если задано некратное четырем число вершин, то остаток игнорируется.

•GL_POLYGON — последовательно задаются вершины выпуклого
-----------------------------
Функция glVertex задаёт вершины к примеру:
glVertex2d(-1,0); мы взяли вершину -1,0
glVertex2d(1,1); взяли 1,1
Эти вершины объеденились и получилась линия с точки -1,0 до 1,1.

Вот мы подходим к завершению, давайте более сложный пример - нарисуем трёхмерный триугольник с квадратом.
Синтаксис такой:
#include // needed in order to have "exit" function @@@
#include

#include //Подключаем нужные библиотеки для работы с OpenGL
#include //Библиотека GLUT
#include //Библиотека GLU, которую мы будем использовать позже.


GLfloat rtri; // Угол для треугольник
GLfloat rquad; // Угол для четырехугольника
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Очистка экрана и буфера глубины
glLoadIdentity(); /// Сбросить текущую матрицу
glTranslatef(-1.5f,0.0f,-6.0f); // Сдвиг влево и вглубь экрана
glRotatef(rtri,0.0f,1.0f,0.0f); // Вращение пирамиды по оси Y

glBegin(GL_TRIANGLES); // Начало рисования пирамиды
glColor3f(1.0f,0.0f,0.0f); // Красный
glVertex3f( 0.0f, 1.0f, 0.0f); // Верх треугольника (Передняя)
glColor3f(0.0f,1.0f,0.0f); // Зеленный
glVertex3f(-1.0f,-1.0f, 1.0f); // Левая точка
glColor3f(0.0f,0.0f,1.0f); // Синий
glVertex3f( 1.0f,-1.0f, 1.0f); // Правая точка
glColor3f(1.0f,0.0f,0.0f); // Красная
glVertex3f( 0.0f, 1.0f, 0.0f); // Верх треугольника (Правая)
glColor3f(0.0f,0.0f,1.0f); // Синия
glVertex3f( 1.0f,-1.0f, 1.0f); // Лево треугольника (Правая)
glColor3f(0.0f,1.0f,0.0f); // Зеленная
glVertex3f( 1.0f,-1.0f, -1.0f); // Право треугольника (Правая)
glColor3f(1.0f,0.0f,0.0f); // Красный
glVertex3f( 0.0f, 1.0f, 0.0f); // Низ треугольника (Сзади)
glColor3f(0.0f,1.0f,0.0f); // Зеленный
glVertex3f( 1.0f,-1.0f, -1.0f); // Лево треугольника (Сзади)
glColor3f(0.0f,0.0f,1.0f); // Синий
glVertex3f(-1.0f,-1.0f, -1.0f); // Право треугольника (Сзади)
glColor3f(1.0f,0.0f,0.0f); // Красный
glVertex3f( 0.0f, 1.0f, 0.0f); // Верх треугольника (Лево)
glColor3f(0.0f,0.0f,1.0f); // Синий
glVertex3f(-1.0f,-1.0f,-1.0f); // Лево треугольника (Лево)
glColor3f(0.0f,1.0f,0.0f); // Зеленный
glVertex3f(-1.0f,-1.0f, 1.0f); // Право треугольника (Лево)
glEnd();
glLoadIdentity();
glTranslatef(1.5f,0.0f,-7.0f); // Сдвинуть вправо и вглубь экрана
glRotatef(rquad,1.0f,1.0f,1.0f); // Вращение куба по X, Y & Z
glBegin(GL_QUADS); // Рисуем куб
glColor3f(0.0f,1.0f,0.0f); // Синий
glVertex3f( 1.0f, 1.0f,-1.0f); // Право верх квадрата (Верх)
glVertex3f(-1.0f, 1.0f,-1.0f); // Лево верх
glVertex3f(-1.0f, 1.0f, 1.0f); // Лево низ
glVertex3f( 1.0f, 1.0f, 1.0f); // Право низ
glColor3f(1.0f,0.5f,0.0f); // Оранжевый
glVertex3f( 1.0f,-1.0f, 1.0f); // Верх право квадрата (Низ)
glVertex3f(-1.0f,-1.0f, 1.0f); // Верх лево
glVertex3f(-1.0f,-1.0f,-1.0f); // Низ лево
glVertex3f( 1.0f,-1.0f,-1.0f); // Низ право
glColor3f(1.0f,0.0f,0.0f); // Красный
glVertex3f( 1.0f, 1.0f, 1.0f); // Верх право квадрата (Перед)
glVertex3f(-1.0f, 1.0f, 1.0f); // Верх лево
glVertex3f(-1.0f,-1.0f, 1.0f); // Низ лево
glVertex3f( 1.0f,-1.0f, 1.0f); // Низ право
glColor3f(1.0f,1.0f,0.0f); // Желтый
glVertex3f( 1.0f,-1.0f,-1.0f); // Верх право квадрата (Зад)
glVertex3f(-1.0f,-1.0f,-1.0f); // Верх лево
glVertex3f(-1.0f, 1.0f,-1.0f); // Низ лево
glVertex3f( 1.0f, 1.0f,-1.0f); // Низ право
glColor3f(0.0f,0.0f,1.0f); // Синий
glVertex3f(-1.0f, 1.0f, 1.0f); // Верх право квадрата (Лево)
glVertex3f(-1.0f, 1.0f,-1.0f); // Верх лево
glVertex3f(-1.0f,-1.0f,-1.0f); // Низ лево
glVertex3f(-1.0f,-1.0f, 1.0f); // Низ право
glColor3f(1.0f,0.0f,1.0f); // Фиолетовый
glVertex3f( 1.0f, 1.0f,-1.0f); // Верх право квадрата (Право)
glVertex3f( 1.0f, 1.0f, 1.0f); // Верх лево
glVertex3f( 1.0f,-1.0f, 1.0f); // Низ лево
glVertex3f( 1.0f,-1.0f,-1.0f); // Низ право
glEnd(); // Закончили квадраты

rtri+=0.2f; // Увеличим переменную вращения для треугольника
rquad-=0.15f; // Уменьшим переменную вращения для квадрата
glutSwapBuffers();
//glFlush(); //выводим изображение на экран . Как говорил не обязатильно
}
GLvoid DisplayResize(GLsizei Width, GLsizei Height)
{
if (Height==0) // Предотвращение деления на ноль, если окно слишком мало
Height=1;

glViewport(0, 0, Width, Height); // Сброс текущей области вывода
// и перспективных преобразований

glMatrixMode(GL_PROJECTION); // Выбор матрицы проекций
glLoadIdentity(); // Сброс матрицы проекции

gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
// Вычисление соотношения геометрических размеров для окна
glMatrixMode(GL_MODELVIEW); // Выбор матрицы просмотра модели
}

/* обработчик нажатия кнопки. */
void keyPressed(unsigned char key, int x, int y)
{
switch (key) {
case 'd': /* delta, triangle */
break;
case 'o': /* round */
break;
case 'q': /* square*/
break;
case 'x': /* cross */
break;
case 'n': /* key with the music note */
break;
case 's': /* select */
break;
case 'a': /* startbutton */ /* If START is pressed,
kill everything. */
/* exit the program...normal termination. */
exit(0);
default:
;
}
}

/* обработчик на отпускание кнопки. */
void keyReleased(unsigned char key, int x, int y)
{
switch (key) {
case 'd': /* delta, triangle */
break;
case 'o': /* round */
break;
case 'q': /* square*/
break;
case 'x': /* cross */
break;
case 'n': /* key with the music note */
break;
case 's': /* select */
break;
case 'a': /* startbutton */
break;
default:
;
}
}

int main(int argc, char **argv)
{
glutInit(&argc,argv); //инициализация
glutInitDisplayMode(GLUT_DOUBLE); //установка режима отображения
glutInitWindowSize(480, 272); //размер окна
glutInitWindowPosition(0,0); //позиция окна
glutCreateWindow("Boll"); //заглавие нашего окошка
glutKeyboardFunc(keyPressed);
glutKeyboardUpFunc(keyReleased);
glutReshapeFunc(DisplayResize);
glutDisplayFunc(Display); //функция для рисования
glutMainLoop(); //Запускаем обработчик событий
return 1;
}




Функция glLoadIdentity сбрасывает состояние матрицы
Функция glTranslatef перемещает точку по заданой траектории 
Пример:
glTranslatef(-1.5f,0.0f,-6.0f); // Сдвинемся влево на 1.5 единицы и
// в экран на 6.0

Теперь у нас есть объект, изображенный в правой части экрана. Нам нужно переместиться в левую часть.
Так как мы в прошлый раз перемещались влево на 1.5 единицы, необходимо переместиться на 3.0
единицы вправо (1.5 единицы – это будет центр, плюс еще 1.5 единицы для правого края).
glTranslatef(3.0f,0.0f,0.0f); // Сдвинем вправо на 3 единицы



Скачать примеры:
пример 1
пример 2

В архиве идёт как исходник так и уже откомпилированная программа.

Вот и всё) В следующем уроке мы научимся налаживать,фильтрировать и освещать текстуры, если успею, ходить по пространству.

Всем спасибо за внимание!

Вернуться назад