Разработка игрового движка с нуля, как создать игровой движок |
Здравствуйте, гость ( Авторизация | Регистрация )
Разработка игрового движка с нуля, как создать игровой движок |
27.12.2017, 18:46
Сообщение
#101
|
|
Почти Игроман Репутация: 187 Группа: Участник Сообщений: 647 Награды: 4 Регистрация: 05.05.2007 |
refuse, это что, для каждой текстуры нужен будет такой скрипт? Я в своём движке думаю сделать больше похоже на сталкерский: у каждой модели есть список текстур и "шейдер" ( конечно, у меня там FFP =) ). В каждой стадии шейдера хранится индекс текстуры из этого списка. Таким образом можно уменьшить количество переключений состояния, т.к. текстуры можно менять отдельно.
|
 
|
|
|
|
27.12.2017, 21:36
Сообщение
#102
|
|
Геймер Репутация: 86 Группа: Участник Сообщений: 128 Награды: 4 Регистрация: 05.05.2012 |
это что, для каждой текстуры нужен будет такой скрипт? Немного сложнее. Каждый скрипт будет описывать некую схему в виде определенного набора констант, на основании этой схемы будут компилироваться шейдера. Из этого набора констант будут вычисляться хеши которым будут сопоставлены скомпилированные шейдера, и при парсинге следующего материала ему будет назначен уже скомпилированный шейдер, если материал с подобной структурой уже был загружен. В самом скрипте материала используемый шейдер явно не будет указываться. Glsl шейдер будет всего один, но он будет порезан на секции заключенные в блоки #ifdef ... #elif ... #else, перед компиляцией шейдера строка с исходным кодом шейдера будет добавляться к строке с заголовком, в котором и будут объявлены соответствующие дефайны. Этот заголовок будет строиться при парсинге материала и из него же будет вычисляться хеш. Вместе с этим каждый материал будет содержать наборы значений констант, а кроми этого он может быть мультипроходным (привет шкурке, которая отрисовывается в несколько проходов) и состоять из нескольких стейджей для которых могут быть использованы разные шейдера. Сложновато? Я тоже так думаю, полагаю на реализацию все новогодние каникулы уйдут (в перерывах между пьянством) если не больше >_< Я в своём движке думаю сделать больше похоже на сталкерский: у каждой модели есть список текстур и "шейдер" В описанном тобой варианте этот список хранится не в отдельном файле с материалом, а непосредственно в файле модели, вот и вся разница. Причем у некоторых моделей эти параметры могут совпадать, а выделение материала в отдельную сущность позволяет их переиспользование. Ну и как вариант, впоследствии, когда появится редактор, можно будет скрипт материала записывать непосредственно в файл модели. Таким образом можно уменьшить количество переключений состояния, т.к. текстуры можно менять отдельно. Сортировку я представляю себе примерно так: при сортировке оперируют более мелкими сущностями которые получаются в результате дробления материалов и моделей на кусочки геометрии, текстуры, шейдера трансформации, и объединения их в рендер таргеты, которые помещаются в очередь отрисовки в определенном порядке. Но до этого еще ой как далеко и много другой работы предстоит проделать -------------------- nop
|
 
|
|
28.12.2017, 04:33
Сообщение
#103
|
|
Почти Игроман Репутация: 187 Группа: Участник Сообщений: 647 Награды: 4 Регистрация: 05.05.2007 |
Сложновато? Да, и в правду сложновато. Но всё-таки, для каждой текстуры свои константы. Это же сколько лишней информации может быть. А если что-то поменять в этих константах потребуется для набора аналогичных текстур? Может добавить возможность наследования настроек из материала-шаблона? |
 
|
|
28.12.2017, 05:53
Сообщение
#104
|
|
Игровой Эксперт Репутация: 407 Группа: Участник Сообщений: 2394 Награды: 5 Регистрация: 19.01.2009 |
У меня JSON используется, формат примерно на твой похож.
Pass { Shader ... Defines ... { Diffuse .... Specular ... Metallness ... ... ... PBR PARAMS{ ... } Bump params {...} } } Вообще по хорошему еще бы дописать добавить сразу RT, что бы рендер сам подстраивался под материал |
 
|
|
08.01.2018, 17:03
Сообщение
#105
|
|
Геймер Репутация: 86 Группа: Участник Сообщений: 128 Награды: 4 Регистрация: 05.05.2012 |
Начал реализовывать задуманную систему материалов и в процессе понял, что такая система не должна использоваться в движке, слишком долго будут парситься материалы и компилироваться шейдера. Решил отказаться от нее и использовать набор заранее скомпилированных шейдеров, которые будут явно указываться в скрипте материала, как описано в сообщении выше. Чуть позже покажу результаты.
P.S. потер собственный флуд, дабы не загромождать тему бесполезной информацией. -------------------- nop
|
 
|
|
08.01.2018, 18:22
Сообщение
#106
|
|
Игровой Бог Репутация: 648 Группа: Участник Сообщений: 5354 Награды: 9 Регистрация: 24.09.2010 |
Любое начинание это конечно хорошо, да и вам самим решать в любом случае, но не попахивает ли это все велосипедом? Чем потенциальный движок будет отличаться от существующих аналогов? Не думаете ли вы, что все эти этапы проектирования архитектуры были уже пройдены многими разработчиками и в итоге получится примерно тоже самое?
-------------------- |
 
|
|
08.01.2018, 18:34
Сообщение
#107
|
|
Доктор Игровых Наук Репутация: 544 Группа: Участник Сообщений: 3657 Награды: 9 Регистрация: 12.07.2007 |
слишком долго будут парситься материалы и компилироваться шейдера Сделай кэширование, первое обращение к нему и он компилится, в последующем берется из кэша, так делают очень многие. Дабы избавиться от парсинга можно тоже уйти к унификации путем компиляции в малый бинарный формат по одному из 2х путей: 1) Компилятор из твоих json во что то бинарное для снижения размеров и времени загрузки\парсинга. Далее простой компилятор который берет некий уровень, пробегает по всем материалам и составляет список. Дальше по этому списку делаешь 1 файл со всеми материалами. Далее этот файл разово грузится вместе с уровнем и выгружается при смене уровня. 2) Решение в лоб, делаешь список материалов уровня, и перед загрузкой делаешь их компил. Этакий прекэшинг тупой. Касательно решения возможного вопроса "а если материалы настраивать то надо сначало чистить кэш что не удобно", тут уже тоже все придумано как то так: - нужно загрузить материал Х - считаем sha(любой удобный быстрый алгоритм, хоть мд5) материала - смотрим в %TEMP%\YouGame\cache\хэш - если есть берем его, нет значит компилим\рекомпилим и сохраняем в кэш PS компиляция шейдера на целевой точка игрока это очень хорошо на самом деле. Сообщение отредактировал jamakasi - 08.01.2018, 18:35 |
 
|
|
08.01.2018, 20:19
Сообщение
#108
|
|
Игровой Эксперт Репутация: 407 Группа: Участник Сообщений: 2394 Награды: 5 Регистрация: 19.01.2009 |
Начал реализовывать задуманную систему материалов и в процессе понял, что такая система не должна использоваться в движке, слишком долго будут парситься материалы и компилироваться шейдера. ShaderCache в помощь слишком долго будут парситься материалы и компилироваться шейдера Сделай кэширование, первое обращение к нему и он компилится, в последующем берется из кэша, так делают очень многие. Дабы избавиться от парсинга можно тоже уйти к унификации путем компиляции в малый бинарный формат по одному из 2х путей: 1) Компилятор из твоих json во что то бинарное для снижения размеров и времени загрузки\парсинга. Далее простой компилятор который берет некий уровень, пробегает по всем материалам и составляет список. Дальше по этому списку делаешь 1 файл со всеми материалами. Далее этот файл разово грузится вместе с уровнем и выгружается при смене уровня. 2) Решение в лоб, делаешь список материалов уровня, и перед загрузкой делаешь их компил. Этакий прекэшинг тупой. Касательно решения возможного вопроса "а если материалы настраивать то надо сначало чистить кэш что не удобно", тут уже тоже все придумано как то так: - нужно загрузить материал Х - считаем sha(любой удобный быстрый алгоритм, хоть мд5) материала - смотрим в %TEMP%\YouGame\cache\хэш - если есть берем его, нет значит компилим\рекомпилим и сохраняем в кэш PS компиляция шейдера на целевой точка игрока это очень хорошо на самом деле. И очень плохо на самом деле, если капнуть глубже. Берем SPIR-V/HLSL, генерируем байт-код. Упаковываем этот байт-код и отдаем пользователю. PROOOFIIIT! В случае с чистым GLSL, то только вариант парсинга прокатит. Можно сделать кэширование, но оно совсем не безупречно работает. 1) Генерация шейдерного кэша, происходит при первом обращении. 2) Если обновился драйвер - шейдерный кэш устаревает, необходима перегенерация. Либо использовать трюк с конвертацией HLSL байт-кода в GLSL |
 
|
|
08.01.2018, 20:25
Сообщение
#109
|
|
Геймер Репутация: 86 Группа: Участник Сообщений: 128 Награды: 4 Регистрация: 05.05.2012 |
Любое начинание это конечно хорошо, да и вам самим решать в любом случае, но не попахивает ли это все велосипедом? Чем потенциальный движок будет отличаться от существующих аналогов? Не думаете ли вы, что все эти этапы проектирования архитектуры были уже пройдены многими разработчиками и в итоге получится примерно тоже самое? Я планирую создать некий Фреймворк с конечной целью сделать какую-нибудь игру. Конечно, можно было бы взять какое-нибудь готовое решение и сконцентрироваться на создании именно игры, но мне этот вариант не подходит по нескольким причинам: 1. Мне интересно не игру делать, интересен сам процесс ее создания, т.е. кодить и придумывать архитектурные решения самостоятельно. 2. Использование сторонней технологии предполагает 80-90% нудной рутины и лишь 10-20% творческой работы. Этого добра мне хватает на основной работе. В общем считайте что это хобби, некоторые читают книги после работы, некоторые в качалку ходят, а я вот велосипед строю jamakasi, от идеи генерировать шейдера на основе набора данных определяемых скриптом я не отказался, просто такую систему логичнее реализовать в виде отдельного инструмента, который на вход получает скрипт, а на выходе генерирует шейдера и скрипт материала. при помощи такого инструмента можно заранее создать библиотеку шейдеров, которые будут использоваться движком. Движок будет только парсить скрипты материалов и использовать указанные в них шейдера. Upd. Вот результаты моих экспериментов с убершейдером: shader.h Код namespace Engine { class Shader { public: virtual ~Shader() {} virtual void Compile(const String &shaderName, bool isDynamic) = 0; virtual void Draw(GraphicsShapePtr shape) = 0; virtual void SetWorldViewProj(const Matrix4 &worldViewProj) = 0; }; Shader *GetShader(); } shader.cpp Код const char geomVertexShader[] = "\ layout(location = 1) in vec4 vertexPosition;\n\ layout(location = 2) in vec4 vertexNormal;\n\ layout(location = 3) in vec4 vertexTangent;\n\ layout(location = 4) in vec4 vertexBinormal;\n\ layout(location = 5) in vec2 vertexTexcoords;\n\ layout(location = 6) in uint bones;\n\ layout(location = 7) in vec4 weights;\n\ out vec2 texCoords;\n\ uniform mat4 worldViewProj;\n\ void main()\n\ {\n\ gl_Position = worldViewProj * vertexPosition;\n\ texCoords = vertexTexcoords;\n\ }\n\ \0"; const char geomFragmentShader[] = "\ in vec2 texCoords;\n\ out vec4 color;\n\ #ifdef DIFFUSE_MAP\n\ uniform sampler2D diffuseMap;\n\ #endif\n\ void main()\n\ {\n\ #ifdef DIFFUSE_MAP\n\ color = texture(diffuseMap, texCoords);\n\ #else\n\ color = vec4(1.0, 1.0, 1.0, 1.0);\n\ #endif\n\ }\n\ \0"; namespace Engine { // Shader parameters const String tagDiffuseMap = "DiffuseMap"; // Macro definitions const String tagDiffuseMapMacro = "DIFFUSE_MAP"; const String tagDynamicMacro = "DYNAMIC"; struct ShaderDataHolder { ShaderDataHolder(const ShaderDataHolder &) = delete; ShaderDataHolder &operator = (const ShaderDataHolder &) = delete; ShaderDataHolder() : gpuProgramId(INVALID_GPU_PROGRAM_ID) { } ShaderDataHolder(GpuProgramId gpuProgramId) : gpuProgramId(gpuProgramId) { } ~ShaderDataHolder() { if (gpuProgramId != INVALID_GPU_PROGRAM_ID) GetRenderer()->DestroyGpuProgram(gpuProgramId); } GpuProgramId gpuProgramId; }; using ShaderDataHolderPtr = std::shared_ptr<ShaderDataHolder>; using ShaderDataMap = std::unordered_map<String, ShaderDataHolderPtr>; struct Material { TexturePtr diffuseMap; }; using MaterialPtr = std::shared_ptr<Material>; using MaterialMap = std::unordered_map<String, MaterialPtr>; template <class AssociativeContainer_T, class Key_T> bool HasEntry(const AssociativeContainer_T &container, const Key_T &key) { auto elementIt = container.find(key); if (elementIt != container.end()) return true; return false; } template <class Key_T, class Val_T> bool GetEntry(const std::unordered_map<Key_T, Val_T> &container, const Key_T &key, Val_T &value) { auto elementIt = container.find(key); if (elementIt == container.end()) return false; value = elementIt->second; return true; } class ShaderImpl : public Shader { public: ShaderImpl() { worldViewProj.SetIdentity(); staticShaders.reserve(MAX_SHADER_DATAS); dynamicShaders.reserve(MAX_SHADER_DATAS); } virtual void Compile(const String &shaderName, bool isDynamic) override { if (shaderName.empty()) { //TODO: log error return; } if ((isDynamic && HasEntry(dynamicShaders, shaderName)) || (!isDynamic && HasEntry(staticShaders, shaderName))) { //TODO: log warning return; } ByteArray source; if (!LoadFileSource(shaderName + ".shader", source)) { //TODO: log error return; } ScriptPtr shaderScript = ParseScript(shaderName, source); if (!shaderScript) { //TODO: log error return; } String shaderIdentifierStr; const String shaderBase = MakeShaderBase(shaderScript, isDynamic, shaderIdentifierStr); const String vertexShaderStr = String(shaderBase) + geomVertexShader; const String fragmentShaderStr = String(shaderBase) + geomFragmentShader; ByteArray vertexShaderSrc(vertexShaderStr.begin(), vertexShaderStr.end()); ByteArray fragmentShaderSrc(fragmentShaderStr.begin(), fragmentShaderStr.end()); GpuProgramId gpuProgramId = GetRenderer()->CreateGpuProgram(vertexShaderSrc, fragmentShaderSrc); if (gpuProgramId == INVALID_GPU_PROGRAM_ID) { //TODO: log error return; } if (!ParseMaterial(shaderScript, )) auto shaderData = std::make_shared<ShaderDataHolder>(gpuProgramId); if (isDynamic) dynamicShaders[shaderIdentifierStr] = shaderData; else staticShaders[shaderIdentifierStr] = shaderData; } virtual void Draw(GraphicsShapePtr shape) override { ShaderDataHolderPtr shaderData; bool isDynamic = IsDynamicShape(*shape); if ((isDynamic && !GetEntry(dynamicShaders, shape->shaderName, shaderData)) || (!isDynamic && !GetEntry(staticShaders, shape->shaderName, shaderData))) { //TODO: log warning return; } if (shaderData != currentShaderData) { currentShaderData = shaderData; GetRenderer()->BindGpuProgram(shaderData->gpuProgramId); if (shaderData->diffuseMap) GetRenderer()->BindHardwareTexture(shaderData->diffuseMap->GetHardwareTexture(), DIFFUSE_MAP); GetRenderer()->SetGpuProgramConstant(shaderData->gpuProgramId, "worldViewProj", worldViewProj); } GetRenderer()->DrawHardwareBuffer(shape->hardwareBufferId); } virtual void SetWorldViewProj(const Matrix4 &worldViewProj) override { this->worldViewProj = worldViewProj; if (currentShaderData) { GetRenderer()->SetGpuProgramConstant( currentShaderData->gpuProgramId, "worldViewProj", worldViewProj); } } private: bool LoadFileSource(const String &fileName, ByteArray &source) const { FilePtr file = GetFileSystem()->OpenFileRead(fileName); if (!file) return false; const size_t fileSize = file->GetSize(); if (fileSize == 0) return false; source.resize(fileSize); file->Read(&source[0], source.size()); return true; } String MakeMacroDefinition(const String ¯oName, const String ¯oDefinition) const { return String("#ifndef ") + macroName + '\n' + String("#define ") + macroName + ' ' + macroDefinition + '\n' + String("#endif") + "\n\n"; } String MakeShaderBase(ScriptPtr shaderScript, bool isDynamic, String &usedDefinesString) const { String result = "#version 330\n\n"; const String separator = ";"; if (isDynamic) { result += MakeMacroDefinition(tagDynamicMacro, "1"); usedDefinesString += tagDynamicMacro + separator; } if (shaderScript->HasValue(tagDiffuseMap)) { result += MakeMacroDefinition(tagDiffuseMapMacro, "1"); usedDefinesString += tagDiffuseMapMacro + separator; } return result; } bool IsDynamicShape(const GraphicsShape &shape) const { switch (shape.vertexFormat) { case VA_BONES: case VA_SKINNED_W2: case VA_SKINNED_W3: case VA_SKINNED_W4: case VA_SKINNED_W1: return true; } return false; } bool ParseMaterial(ScriptPtr shaderScript, Material &material) const { const String diffuseMapName = shaderScript->GetString(tagDiffuseMap); if (!diffuseMapName.empty()) material.diffuseMap = GetTextureManager()->GetTexture(diffuseMapName); return true; } private: static const int MAX_SHADER_DATAS = 128; Matrix4 worldViewProj; ShaderDataMap staticShaders; ShaderDataMap dynamicShaders; ShaderDataHolderPtr currentShaderData; }; Shader *GetShader() { static ShaderImpl shader; return &shader; } } Я планировал сделать отдельный кэш для GLSL программ на основе unordered_map, а в качестве ключа использовать строку с перечислением использованных дефайнов, отсортированных в алфавитном порядке. Сообщение отредактировал refuse - 08.01.2018, 20:51 -------------------- nop
|
 
|
|
08.01.2018, 22:52
Сообщение
#110
|
|
Игровой Эксперт Репутация: 407 Группа: Участник Сообщений: 2394 Награды: 5 Регистрация: 19.01.2009 |
Цитата Я планировал сделать отдельный кэш для GLSL программ на основе unordered_map, а в качестве ключа использовать строку с перечислением использованных дефайнов, отсортированных в алфавитном порядке. Зачем так усложнять? Если этот кеш нигде храниться не будет. Используй id шейдера/программы и все Тебе именно охото в рендер вкатиться? - просто взял бы какой-нибудь рендер фреймворк и не парился. |
 
|
|
08.01.2018, 23:08
Сообщение
#111
|
|
Геймер Репутация: 86 Группа: Участник Сообщений: 128 Награды: 4 Регистрация: 05.05.2012 |
Зачем так усложнять? Если этот кеш нигде храниться не будет. Используй id шейдера/программы и все Ну под кэшем я и подразумеваю мапу ключ/id шейдера, т.е. обычный менеджер. Если по соответствующему ключу записей в мапе не найдено, то компилируется новый шейдер, если запись существует, то используется существующий. Тебе именно охото в рендер вкатиться? - просто взял бы какой-нибудь рендер фреймворк и не парился Да, мне интересно самому во всех тонкостях разбираться. -------------------- nop
|
 
|
|
13.01.2018, 03:05
Сообщение
#112
|
|
Геймер Репутация: 86 Группа: Участник Сообщений: 128 Награды: 4 Регистрация: 05.05.2012 |
Реализовал зачатки системы материалов (или скорее шейдерную систему, если такое название вообще применимо). За основу все-таки взял убершейдер. Система пока ничего не умеет делать, т.к. мне пока самому непонятно какие параметры должны быть у шейдера и как их организовать, так что в текущем виде скрипт шейдера имеет только один параметр:
Код { // Diffuse map DiffuseMap = cat_diff; } Следующим этапом планирую реализовать отложенное затенение и углубляться потихонечку в тонкости шейдерных эффектов. P.S. Ну и напоследок немного котика с отключенным буфером глубины Кстати, код тестового приложения заметно сократился: + Код #include <engine/model.h> #include <engine/platform.h> #include <engine/renderer.h> #include <engine/shader.h> int main() { Engine::Platform *platform = Engine::GetPlatform(); if (!platform->CreateMainWindow()) return -1; Engine::Renderer *renderer = Engine::GetRenderer(); Engine::ModelManager *modelManager = Engine::GetModelManager(); Engine::ModelPtr teapot = modelManager->GetModel("teapot"); Engine::Matrix4 rotation(Engine::Matrix3(M_PI * 0.25f, M_PI * 0.1f, 0)); Engine::Matrix4 proj, view, model, modelViewProj; view.SetIdentity(); proj.SetPerspective(1.3f, 1.78f, 0.1f, 100000.0f); model.SetTranslation(Engine::Vector3(-0.35, -0.3f, -1.5f)); modelViewProj = rotation * model * proj; Engine::Shader *shader = Engine::GetShader(); shader->Compile("cat", false); shader->SetWorldViewProj(modelViewProj); platform->SetMainWindowSize(1280, 720, false); renderer->SetViewport(1280, 720); for (;;) { if (!platform->UpdateMainWindow()) { break; } shader->Draw(teapot->GetGraphicsShapes()[0]); } } -------------------- nop
|
 
|
|
14.01.2018, 21:47
Сообщение
#113
|
|
Геймер Репутация: 86 Группа: Участник Сообщений: 128 Награды: 4 Регистрация: 05.05.2012 |
Снова размышлял над тем какую бы игру можно было бы сделать используя собственный графический движок, подумал про про бильярд. 3-х мерное окружение с качественными текстурами и освещением + честная физика + мультиплеер.
Я уже даже прикинул какой стек технологий можно использовать: В качестве фронтенда мобильное приложение, скажем под андроид, естественно с использованием самописного движка. В качестве бэкенда самописный сервер на С++, который будет осуществлять авторизацию, регистрацию, матчмейкинг и соблюдение игровых правил (определение очередности хода, подсчет очков и т.д). Также предусмотреть самописный REST, скажем на php, для предоставления некоего АПИ для внешних сервисов (регистрация с сайта/форума, осуществление внутриигровых покупок и т.п.) В качестве БД PostgreSQL или MySQL Server. Вобщем фантазирую Единственной серьезной проблемой является тот факт, что идея не нова и стор должен быть забит похожими играми. Примеры: -------------------- nop
|
 
|
|
14.01.2018, 22:18
Сообщение
#114
|
|
Мастер Игры Репутация: 120 Группа: Участник Сообщений: 1387 Награды: 4 Регистрация: 29.04.2009 |
Единственной серьезной проблемой является тот факт, что идея не нова и стор должен быть забит похожими играми. Примеры: В основном все делают пул по идее. А как насчет русского бильярда? А вообще на NES был Lunar Ball... занятная игрушка, в которой можно было менять гравитацию, а сами столы имели причудливую форму. Было бы прикольно что то такое увидеть в 3D. -------------------- "Лови отвальную, фраер."
|
 
|
|
14.01.2018, 22:38
Сообщение
#115
|
|
Опытный Геймер Репутация: 9 Группа: Участник Сообщений: 161 Регистрация: 23.12.2017 |
Снова размышлял над тем какую бы игру можно было бы сделать используя собственный графический движок, подумал про про бильярд. 3-х мерное окружение с качественными текстурами и освещением + честная физика + мультиплеер. Я уже даже прикинул какой стек технологий можно использовать: В качестве фронтенда мобильное приложение, скажем под андроид, естественно с использованием самописного движка. В качестве бэкенда самописный сервер на С++, который будет осуществлять авторизацию, регистрацию, матчмейкинг и соблюдение игровых правил (определение очередности хода, подсчет очков и т.д). Также предусмотреть самописный REST, скажем на php, для предоставления некоего АПИ для внешних сервисов (регистрация с сайта/форума, осуществление внутриигровых покупок и т.п.) Вобщем фантазирую Единственной серьезной проблемой является тот факт, что идея не нова и стор должен быть забит похожими играми. Я тут по-быстрому накидал один вариант: Сообщение отредактировал solitary.wanderer94 - 14.01.2018, 22:43 |
 
|
|
15.01.2018, 00:21
Сообщение
#116
|
|
Мастер Игры Репутация: 120 Группа: Участник Сообщений: 1387 Награды: 4 Регистрация: 29.04.2009 |
Кстати, раз форум по большей части сталкеру посвящен, да и что то уникальное хочется сделать, то было бы прикольно увидеть бильярд в Зоне. Как раз столики на старом Баре были, да вот в игре не использовались... Ну а что? Было бы здорово: сталкеры играют в бильярд, фоном слышны анекдоты и музыка... атмосферно и действительно уникально.
-------------------- "Лови отвальную, фраер."
|
 
|
|
15.01.2018, 00:27
Сообщение
#117
|
|
Опытный Геймер Репутация: 9 Группа: Участник Сообщений: 161 Регистрация: 23.12.2017 |
|
 
|
|
21.01.2018, 23:22
Сообщение
#118
|
|
Геймер Репутация: 86 Группа: Участник Сообщений: 128 Награды: 4 Регистрация: 05.05.2012 |
Реализованы основы техники отложенного затенения, пока только geometry pass. Собственно затенение (lightning pass) будет добавлено позже.
Содержимое G-Buffer'а с котиком (цвет, текстурные координаты, нормали, позиции соответственно) Следующим этапом планирую добавить источники света и собственно затенение. P.S. Кстати, столкнулся с таким странным поведением OpenGL - не получается изменить размер RT в большую сторону. В меньшую - пожалуйста, а в большую конечный результат обрезается вот таким образом Думаю пока что с этим делать. Мб кто-то знает как пофиксить? -------------------- nop
|
 
|
|
22.01.2018, 05:30
Сообщение
#119
|
|
Игровой Эксперт Репутация: 407 Группа: Участник Сообщений: 2394 Награды: 5 Регистрация: 19.01.2009 |
Размер буфера фиксирован же, вроде как. Насколько мне помнится не больше 4 текстур.
|
 
|
|
22.01.2018, 07:49
Сообщение
#120
|
|
Продвинутый геймер Репутация: 38 Группа: Участник Сообщений: 392 Награды: 4 Регистрация: 11.04.2015 |
|
 
|
|
Текстовая версия | Сейчас: 02.05.2024, 19:07 |