
Читать | Комментарии [1]
2 ноя. 2007
Читать | Комментарии [1]
2 ноя. 2007
Лежит тут: https://github.com/nsf/nextgame
Работа над движком продолжается. Недавно тыкал систему материалов в рендере ( http://imgur.com/a/Ui6OD ), тени ( http://imgur.com/a/vcI9E ), ну и всякое разное.
http://i.imgur.com/CJqMosa.jpg
http://i.imgur.com/SgjGtgb.jpg
Ссылка
27 сен. 2014
Изначально идея была взять воксели и их использовать в качестве материала для LOD механизма. Т.е. упрощать не отображаемую геометрию, а исходный материал. Думал это будет полезно и для сетевой игры - меньше передавать данных по кабелю. Но оказалось, что как только я упрощаю "воксельные поля", они становятся на 100% непригодными для работы с игровой логикой. Кроме этого, "продвинутые" алгоритмы визуализации вокселей усложняют работу с ними. Так например в популярных ныне двойственных алгоритмах, для того чтобы получить информацию об одном генерируемом кваде, нужно рассмотреть 3х3х2 вокселей (18), когда как в старом marching cubes достаточно лишь 8 вокселей. У всех алгоритмов есть плюсы и миунсы. Двойственные алгоритмы генерируют более качественный mesh, но он по умолчанию non-manifold, т.е. топологию качественной не назовешь, это решается дополнительными усилиями. MC хорош тем, что генерирует исключительно manifold геометрию, но у него имеются проблемы с вырожденными треугольниками. Все решаемо.
На данный момент решил вернутся к базовому marching cubes и добавить пост-процессинг для упрощения геометрии. Результаты пока впечатляют, скоро перейду непосредственно к работе с LOD механизмами и логикой редактирования. Привожу пару скриншотов работы алгоритма упрощения геометрии: http://imgur.com/a/LFEBr. Результат убедителен, уменьшая количество треугольников в 12.75 раз, визуальное качество практически остается не тронутым. Конечно, нужно понимать, что это карта с одним материалом, но с другой стороны приведенная геометрия достаточно капризная, реальный ландшафт будет чуток проще.
Ссылка
29 апр. 2014
Мысль дня. Навеяло чужим говнокодом. Свой не пахнет.
Ссылка
16 мар. 2014
Vimeo: https://vimeo.com/83150040
Что на видео? Если вкратце, то ничего продвинутого. Ландшафт представлен в виде воксельных данных, генерируется простой комбинацией 3ех самплов разного перлин нойса (один из самплов из 3д перлин нойса). Геометрия генерируется алгоритмом naive surface nets. Как видно в видео имеется поддержка разных уровней детализации. И базовая система чанков с возможностью распараллеливания работы на несколько потоков. Что касается рендера там совсем ничего интересного. Перлин нойс небо на шейдерах, освещение из фиксированного источника, считается только диффузная часть, нормал мап никаких нет пока что, текстуринг обычный трипланарный.
Видео записано на i5-3470, с использованием 3ех рабочих потоков. Видяшка вполне обычная gtx560.
За время работы накопилось много опыта касательно данной темы, которым хочется поделиться. Но когда и как я его оформлю пока не ясно. Может напишу статью, может таки сделаю видео с комментариями. Что-то точно сделаю, вопрос лишь когда.
Ссылка | Комментарии [3]
1 янв. 2014
Думаю сделать видео с комментами в ближайшем будущем.
Ссылка
7 ноя. 2013
К моему удивлению нашел применение геометрическим шейдерам - per-vertex материал! В вершинный буфер грузим индекс материала. В геометрическом шейдере для каждой вершины создаем два новых вектора из 3 элементов, в первом индексы, во втором вес материала:
vec3(mat[0], mat[1], mat[2]); vec3(1, 0, 0)
vec3(mat[0], mat[1], mat[2]); vec3(0, 1, 0)
vec3(mat[0], mat[1], mat[2]); vec3(0, 0, 1)
Соответственно так побеждается интерполяция и в фрагментном шейдере имеем индекс материала и его вес для каждой вершины в соответствующих каналах этих новых двух векторов. Естественно текстуры храним в Texture Arrays. Не уверен на счет производительности по сравнению с другими методами, но работает. :)
Может кто знает что на эту тему? Хочется per-vertex материал, при этом чтобы геометрия осталась из индексированных вершин.
P.S. Если интересно про то, как делал ландшафт, в комментах отпишитесь, может напишу и про это.
Ссылка | Комментарии [5]
19 окт. 2013
DISCLARIMER: В посте вы часто встретите употребление английских слов и определений вместо русских, на мой взгляд так значительно удобнее для любого программиста, нежели маяться в попытках понять, что я имел ввиду под каким-нибудь дурацким переводом.
constexpr int number = 43; int main(int argc, char **argv) { constexpr int *p = &number; // error: cannot initialize a variable of type 'int *const' with an rvalue of type 'const int *' return 0; }
Любезный clang ясно дает понять, тип указателя p - int *const, вместо ожидаемого const int*. Так вот, собственно первый вывод: constexpr применяется к declaration в целом и не является частью типа. Если constexpr с указателями в общем-то редко используется, знать этот факт вдвойне важно при определении функций. Дело в том, что constexpr не является частью типа возвращаемого значения функции как это было бы в случае с const. Приведу опять пример с указателем и функциями, где этот факт четко иллюстрирован:
int number = 10; constexpr int *number_addr() { return &number; } int main( int argc, char **argv) { int *p = number_addr( ); return 0; }
Вполне компилируется и работает. Согласен, что пример не особо полезен на практике, но позже я покажу почему важно это все знать. Бывают реально случаи, когда указатели используются в constexpr функциях. А пока давайте суммируем наконец то, как constexpr связан с const:
Надеюсь, все выше сказанное помогло в какой-то мере понять каким образом constexpr применяется к сущностям языка и устронило недопомнимание на тему const vs constexpr.
Если коротко, то constexpr следует применять везде, где можно. И если достаточно понятно, когда применять constexpr к переменным и обычным функциям, то применение в классах требует небольшого пояснения. Рассмотрим пример:
class vec3 { union { struct { float _x, _y, _z; }; float _v[3]; }; public: constexpr vec3(): _x( 0), _y( 0), _z( 0) {} // (1) constexpr vec3( float x, float y, float z): _x( x), _y( y), _z( z) {} constexpr vec3( const vec3&) = default; // (2) vec3 &operator=( const vec3&) = default; // (3) constexpr float x( ) { return _x; } // (4) constexpr float y( ) { return _y; } constexpr float z( ) { return _z; } constexpr const float *v( ) { return _v; } // (5) float *v( ) { return _v; } // (6) };
1. С конструкторами я думаю понятно. Если у конструктора пустое тело и есть возможность сделать его constexpr - делайте.
2. Default и delete члены тоже можно объявлять как constexpr. Вполне возможно, что они становятся constexpr при возможности по умолчанию, но для целей самодокументирования никогда не повредит явно указать это.
3. Оператор присвоения обычно не определяется как constexpr, потому что традицонно он возвращает не константную ссылку на *this. А как мы знаем из первой секции этого поста, constexpr делает функцию-член константной (this становится const). Просто не уместно, да и область применения сомнительна, хотя вполне возможно и этот оператор сделать constexpr.
4. Если кто-то еще не понял, объявление constexpr float x() { return _x; } внутри класса эквивалентно float x() const { return _x; }, а поэтому везде где нам хотелось бы определить константную функцию-член и где есть возможность сделать ее constexpr функцией, надо делать.
5. Не забываем важную информацию из первой секции поста - constexpr не влияет на тип возвращаемого значения функции, а поэтому constexpr const сочетание очень даже имеет смысл. Кроме того, т.к. constexpr неявно делает функцию-член константной, мы не можем вернуть float* используя const float _v[3].
6. Еще раз, учитывая, что (5) является константной функией-членом, можно сделать перегрузку для не константного случая, как это традиционно делается в С++ для акцессоров возвращающих указатель или ссылку. Так что все тут вполне корректно.
Ссылка | Комментарии [2]
5 мая 2013
#include <string> #include <cstdio> #include <cstddef> class formatter { const char *format; public: explicit formatter(const char *format): format( format) {} template <typename ...Args> std::string operator( )( Args ...args) { char tmp[4096]; size_t n = std::snprintf( tmp, sizeof( tmp), format, args...); return {tmp, n}; } }; formatter operator "" _fmt( const char *format, size_t) { return formatter( format); } int main( int argc, char **argv) { std::string s = "hello, %s"_fmt( "nsf"); std::string s2 = "%d + %d = %d"_fmt( 1, 2, 3); std::printf( "%s\n", s.c_str( )); std::printf( "%s\n", s2.c_str( )); return 0; }
Стоит отметить, что возможно лучше использовать variadic функцию вместо variadic шаблона - меньше кода компилятор будет генерировать. Но вспоминать va_start/va_end и va_list было лень, поэтому так.
Ссылка
3 мар. 2013