Ландшафт шаг за шагом

Ландшафт шаг за шагом

Ландшафт шаг за шагом.

Этот туториал охватывает азы техники создания ландшафтов. Я рассмотрю карту высот, простейший алгоритм визуализации, раскраску (текстурирование) ландшафта, а так же оптимизацию на уровне OpenGL. Что касается примеров, я считаю, что если размер примера (не считая инициализации OpenGL и т.п.) больше 8-10 Кб то это что угодно, но только не пример. Поэтому я старался следовать этому принципу.

Представление ландшафта, карта высот.

Для представления ландшафта мы будем использовать карту высот.

Карта высот — это двумерный массив значений высот ландшафта, взятых с определенным интервалом. Т.е. мы каждому дискретному значению Х и Y в горизонтальной плоскости сопоставляем высоту.

Графическое представление массива

Создать карту высот можно с помощью графического редактора (прекрасно подойдет Photoshop) или с помощью программ специально для этого предназначенных, например TerraGen. Для создания карты высот в Photoshop’е необходимо выполнить фильтр Clouds, а затем несколько раз фильтр Different clouds. И сохранить полученную картинку как Grayscale файл в формате RAW (в принципе можно использовать другой формат, но об этом поговорим позже). Размер карты может быть произвольный, но удобней использовать квадратную, с размером стороны кратным числу степени двойки: 128х128, 256х256 и т.д.

Будем считать, что на представленных примерах светлые области представляют собой возвышения, соответственно темные — низменность.

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

В примере ландшафт реализован в виде класса Terrain. Приведенный ниже кусочек кода — часть функции Terrain::RenderLandscape() демонстрирует простейшую реализацию процедуры рисования.

Раскраска (текстурирование) ландшафта.

Я расскажу о двух способах закраски ландшафта (и во втором случае текстурирования). Это очень краткое объяснение, не затрагивающее множество аспектов, таких как мультитекстурирование, блендинг нескольких текстур, и т.п. Обо всем этом я напишу отдельную статью.

Способ первый — интерполяция цветов.

Первый способ раскраски ландшафта состоит в использовании интерполяции цветов. Что такое интерполяция цветов, наверное, знает каждый, но если кто-то не знает, краткий пример. Например, если, рисуя треугольник, каждой вершине задать разный цвет, получим картину изображенную ниже. При этом OpenGL сам выполнит интерполяцию, от нас только требуется включить интерполяцию, что делается просто:

ниже приведен код демонстрирующий рисование треугольника с интерполяцией цветов:

Но вернемся к ландшафту. Для раскраски нам опять понадобиться карта, я называю ее картой цветов. Данными для карты цветов служат графические файлы примерно следующего вида:

Грубо говоря, любая RGB картинка сохраненная, как Raw-файл, может быть использована в качестве карты цветов. Важно заметить, что ширина и высота этих файлов должны быть равными размеру карты высот!

Как известно в RGB режиме одна точка изображения описывается тремя байтами — значение Red, Green, Blue компоненты цвета. Таким же образом точки картинки хранятся и в Raw-файле.

Хранить карту цветов (как и карту высот) мы будем в массиве, только в карте высот одному элементу массива соответствует одно значение высоты, а в карте цветов три значения — интенсивность красной, зеленой и синей составляющей цвета. Поэтому введем следующую структуру, которая и будет элементом массива цветов:

При этом процедура рисования (я убрал рисование «проволочной» модели) претерпела минимальные изменения:

Способ второй — текстурирование.

Суть второго способа состоит в наложении на ландшафт текстуры. Для этого нам понадобится текстура, точно такая же как файл для карты цветов, только сохранить его надо как TGA файл (понятно, что выбор формата — это дело вкуса, но в приведенных мною примерах используются TGA-файлы). Я предполагаю что вы знакомы с текстурирование, и не буду описывать процесс загрузки и создания текстуры в OpenGL.

Читайте также:  Кашпо для летних веранд

Размер текстуры, может и не соответствовать размеру карты высот. Например для карты высот 128×128 может быть выбрана текстура размером 64×64 или 512×512 точек. В последнем случае качество буде лучше. На практике достаточно выбрать размер текстуры равный размеру карты высот.

Суть метода состоит в текстурирование одной текстурой всех треугольников нашего ландшафта, рассчитывая каждому треугольнику соответствующие координаты текстуры.

Итак, прежде чем приступить к текстурированию мы должны определит шаг изменения координат текстуры. Необходимо вычислить значение 1.0/MapSize (MapSize — размер карты высот). И тогда умножая координаты x и y треугольника на этот шаг, мы получим координаты текстуры соответствующие этому треугольнику. Ниже приведен рисунок иллюстрирующий текстурирование, и пример процедуры рисования.

Если вы хотите, чтобы текстура не «растягивалась» на весь ландшафт, а соответствовала определенной его части, необходимо при вычислении шага, вместо 1 подставить нужное вам число, и текстура повториться заданное количество раз.

Шаг изменения координат текстуры объявлен как константа в Terrain.H.

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

Следует сказать пару слов о фильтрации текстуры. В приведенном примере используется линейная фильтрация, что намой взгляд, является оптимальным вариантом. Фильтрация ближайший сосед (GL_NEAREST) существенно ухудшает качество, в то время как мипмаппинг практически его не улучшает. Использование мипмаппинга будет иметь смысл, если размер текстуры превышает размер ландшафта хотя бы в два раза, или если текстура повторяется.

Оптимизация на уровне OpenGL

Сразу необходимо заметить, что только оптимизация на уровне OpenGL не может решить проблему производительности в целом. Безусловно, прирост производительности будет но не всегда достаточный. OpenGL оптимизацию необходимо использовать вместе с оптимальным алгоритмом отображения ландшафта. Хотя, если размер карты не превышает 128×128, можно обойтись оптимизацией только на уровне OpenGL.

Первое что можно сделать на уровне OpenGL, это уменьшить число вершин предаваемых на конвейеру текстурирования, для этого необходимо заменить рисование с использованием GL_TRIANGLE_STRIP на эквивалентный GL_TRIANGLE_FAN, как показано на рисунке:

Правда в примере, я использовал GL_TRINGLE_STRIP.

Второе — использовать для отображения функцию glDrawElements. Это функция позволяет рендерить множество примитивов вызовом одной функции, тем самым уменьшая число вызовов OpenGL функций. Для того, что бы выполнить рисование с помощью функции glDrawElements необходимо выполнит три шага:

1. Активировать массивы с данными, в нашем случае массив вершин и координат текстур (аналогично можно поступить с цветами, нормалями и некоторыми другими атрибутами). Для этого необходимо воспользоваться функцией void glEnableClientState(GLenum Array), где в качестве параметра указать GL_VERTEX_ARRAY, а затем GL_TEXTURE_COORD_ARRAY.

2. Задать параметры данных и массивы для их хранения. Для задания массива и типа вершин служит функция

которая определяет способ хранения и координаты вершин. При этом Size определяет число координат вершины (может быть равен 2, 3, 4), Type определяет тип данных (может быть равен GL_SHORT, GL_INT, GL_FLOAT, GL_DOUBLE). Иногда удобно хранить в одном массиве другие атрибуты вершины, и тогда параметр Stride задает смещение от координат одной вершины до координат следующей. Если Stride равен нулю, это значит, что координаты расположены последовательно. В параметре Ptr указывается адрес, где находятся данные.

Аналогично можно определить массив координат текстуры, используя команду

3. Заполнить массивы данными и выполнить отрисовку. Как уже было сказано, рисование выполняется с помощью функции

Mode — указывает какой примитив будет рисоваться, в нашем случае GL_TRIANGLE_STRIP (в принципе может быть любой другой). Count — количество примитивов. Type — тип значений в Indices, может быть GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT или GL_UNSIGNED_INT. В массиве Indices хранятся индексы, в соответствии с которыми данные будут извлекаться из массива вершин и координат текстур.

Читайте также:  Оформление цветочных клумб тюльпаны

Источник статьи: http://masters.donntu.org/2005/fvti/zaharov/libraries/land1.htm

Ландшафт шаг за шагом.

Автор: Серба Андрей

Этот туториал охватывает азы техники создания ландшафтов. Я рассмотрю карту высот, простейший алгоритм визуализации, раскраску (текстурирование) ландшафта, а так же оптимизацию на уровне OpenGL. Что касается примеров, я считаю, что если размер примера (не считая инициализации OpenGL и т.п.) больше 8-10 Кб то это что угодно, но только не пример. Поэтому я старался следовать этому принципу.

Представление ландшафта, карта высот.

Для представления ландшафта мы будем использовать карту высот.

Карта высот — это двумерный массив значений высот ландшафта, взятых с определенным интервалом. Т.е. мы каждому дискретному значению Х и Y в горизонтальной плоскости сопоставляем высоту.


массив


Графическое представление массива

Создать карту высот можно с помощью графического редактора (прекрасно подойдет Photoshop) или с помощью программ специально для этого предназначенных, например TerraGen. Для создания карты высот в Photoshop’е необходимо выполнить фильтр Clouds, а затем несколько раз фильтр Different clouds. И сохранить полученную картинку как Grayscale файл в формате RAW (в принципе можно использовать другой формат, но об этом поговорим позже). Размер карты может быть произвольный, но удобней использовать квадратную, с размером стороны кратным числу степени двойки: 128х128, 256х256 и т.д.

Будем считать, что на представленных примерах светлые области представляют собой возвышения, соответственно темные — низменность.

Photoshop
TerraGen

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

В примере ландшафт реализован в виде класса Terrain. Приведенный ниже кусочек кода — часть функции Terrain::RenderLandscape() демонстрирует простейшую реализацию процедуры рисования.


Пример (40 кБ)

Раскраска (текстурирование) ландшафта.

Я расскажу о двух способах закраски ландшафта (и во втором случае текстурирования). Это очень краткое объяснение, не затрагивающее множество аспектов, таких как мультитекстурирование, блендинг нескольких текстур, и т.п. Обо всем этом я напишу отдельную статью.

Способ первый — интерполяция цветов.

Первый способ раскраски ландшафта состоит в использовании интерполяции цветов. Что такое интерполяция цветов, наверное, знает каждый, но если кто-то не знает, краткий пример. Например, если, рисуя треугольник, каждой вершине задать разный цвет, получим картину изображенную ниже. При этом OpenGL сам выполнит интерполяцию, от нас только требуется включить интерполяцию, что делается просто:

ниже приведен код демонстрирующий рисование треугольника с интерполяцией цветов:

Но вернемся к ландшафту. Для раскраски нам опять понадобиться карта, я называю ее картой цветов. Данными для карты цветов служат графические файлы примерно следующего вида:

Грубо говоря, любая RGB картинка сохраненная, как Raw-файл, может быть использована в качестве карты цветов. Важно заметить, что ширина и высота этих файлов должны быть равными размеру карты высот!

Как известно в RGB режиме одна точка изображения описывается тремя байтами — значение Red, Green, Blue компоненты цвета. Таким же образом точки картинки хранятся и в Raw-файле.

Хранить карту цветов (как и карту высот) мы будем в массиве, только в карте высот одному элементу массива соответствует одно значение высоты, а в карте цветов три значения — интенсивность красной, зеленой и синей составляющей цвета. Поэтому введем следующую структуру, которая и будет элементом массива цветов:

При этом процедура рисования (я убрал рисование «проволочной» модели) претерпела минимальные изменения:

Способ второй — текстурирование.

Суть второго способа состоит в наложении на ландшафт текстуры. Для этого нам понадобится текстура, точно такая же как файл для карты цветов, только сохранить его надо как TGA файл (понятно, что выбор формата — это дело вкуса, но в приведенных мною примерах используются TGA-файлы). Я предполагаю что вы знакомы с текстурирование, и не буду описывать процесс загрузки и создания текстуры в OpenGL.

Читайте также:  Огород во славу божию pdf

Размер текстуры, может и не соответствовать размеру карты высот. Например для карты высот 128×128 может быть выбрана текстура размером 64×64 или 512×512 точек. В последнем случае качество буде лучше. На практике достаточно выбрать размер текстуры равный размеру карты высот.

Суть метода состоит в текстурирование одной текстурой всех треугольников нашего ландшафта, рассчитывая каждому треугольнику соответствующие координаты текстуры.

Итак, прежде чем приступить к текстурированию мы должны определит шаг изменения координат текстуры. Необходимо вычислить значение 1.0/MapSize (MapSize — размер карты высот). И тогда умножая координаты x и y треугольника на этот шаг, мы получим координаты текстуры соответствующие этому треугольнику. Ниже приведен рисунок иллюстрирующий текстурирование, и пример процедуры рисования.

Если вы хотите, чтобы текстура не «растягивалась» на весь ландшафт, а соответствовала определенной его части, необходимо при вычислении шага, вместо 1 подставить нужное вам число, и текстура повториться заданное количество раз.

Шаг изменения координат текстуры объявлен как константа в Terrain.H.

const GLfloat TextureBit = 1.0f/(float)MapSize;
а измененная процедура рисования выглядит следующим образом:

Следует сказать пару слов о фильтрации текстуры. В приведенном примере используется линейная фильтрация, что намой взгляд, является оптимальным вариантом. Фильтрация ближайший сосед (GL_NEAREST) существенно ухудшает качество, в то время как мипмаппинг практически его не улучшает. Использование мипмаппинга будет иметь смысл, если размер текстуры превышает размер ландшафта хотя бы в два раза, или если текстура повторяется.

Оптимизация на уровне OpenGL.

Сразу необходимо заметить, что только оптимизация на уровне OpenGL не может решить проблему производительности в целом. Безусловно, прирост производительности будет но не всегда достаточный. OpenGL оптимизацию необходимо использовать вместе с оптимальным алгоритмом отображения ландшафта. Хотя, если размер карты не превышает 128×128, можно обойтись оптимизацией только на уровне OpenGL.

Первое, что можно сделать на уровне OpenGL, это уменьшить число вершин предаваемых на конвейеру текстурирования, для этого необходимо заменить рисование с использованием GL_TRIANGLE_STRIP на эквивалентный GL_TRIANGLE_FAN, как показано на рисунке:

GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN

Правда в примере, я использовал GL_TRINGLE_STRIP

Второе, использовать для отображения функцию glDrawElements. Это функция позволяет рендерить множество примитивов вызовом одной функции, тем самым уменьшая число вызовов OpenGL функций. Для того, что бы выполнить рисование с помощью функции glDrawElements необходимо выполнит три шага:

1. Активировать массивы с данными, в нашем случае массив вершин и координат текстур (аналогично можно поступить с цветами, нормалями и некоторыми другими атрибутами). Для этого необходимо воспользоваться функцией void glEnableClientState(GLenum Array), где в качестве параметра указать GL_VERTEX_ARRAY, а затем GL_TEXTURE_COORD_ARRAY.

2. Задать параметры данных и массивы для их хранения. Для задания массива и типа вершин служит функция

void glVertexPointer( GLint Size, GLenum Type, GLsizei Stride, void *Ptr )
которая определяет способ хранения и координаты вершин. При этом Size определяет число координат вершины (может быть равен 2, 3, 4), Type определяет тип данных (может быть равен GL_SHORT, GL_INT, GL_FLOAT, GL_DOUBLE). Иногда удобно хранить в одном массиве другие атрибуты вершины, и тогда параметр Stride задает смещение от координат одной вершины до координат следующей. Если Stride равен нулю, это значит, что координаты расположены последовательно. В параметре Ptr указывается адрес, где находятся данные.

Аналогично можно определить массив координат текстуры, используя команду

void glTexCoordPointer(GLint Size, GLenum Type, GLsizei Stride);

3. Заполнить массивы данными и выполнить отрисовку. Как уже было сказано, рисование выполняется с помощью функции

Источник статьи: http://www.gamedev.ru/articles/?id=30015

Оцените статью