Skip to content

Commit b877c97

Browse files
committed
Шейдеры для рандомизации и очистки мира
1 parent edc804f commit b877c97

9 files changed

+161
-81
lines changed

README.md

+7-8
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@
7373
- **T**: Сброс камеры, чтобы весь игровой мир был виден.
7474
- **R**: Случайное заполнение поля.
7575
- **C**: Очистка поля, убить все клетки.
76-
- **I**: Заполнить поле случайным числом фигур.
7776
- **G**: Спрятать или показать сетку.
7877
- **U**: Спрятать или показать пользовательский интерфейс GUI.
7978
- **Y**: Сменить тип мира (сетка с ограниченными краями или с тороидальными краями).
@@ -195,13 +194,13 @@ git push origin v1.0.0
195194

196195
### Будущие улучшения
197196

198-
- [x] Реализация различных наборов правил для "Жизни".
199-
- [x] Управления изменения размера сетки.
200-
- [x] Оптимизация рендеринга для лучшей производительности на больших сетках или более сложных узорах.
201-
- [x] Реализована загрузка паттернов (сложных узорах) из файлов в дириктории `patterns` в формате *.cells*
202-
- [x] Оптимизиция закгрузки и сохранения мира.
203-
- [x] Оптимизация динамической смены размеров мира
204-
- [ ] Оптимизация рандомизации и очистки сетки
197+
- [x] Внедрена поддержка различных наборов правил для игры "Жизнь".
198+
- [x] Добавлена функциональность для динамического изменения размера мира.
199+
- [x] Оптимизирован рендеринг для повышения производительности на больших мирах и сложных узорах.
200+
- [x] Реализована загрузка предустановленных узоров из файлов формата .cells, расположенных в папке patterns.
201+
- [x] Улучшено время загрузки и сохранения состояния мира.
202+
- [x] Ускорена перестройка мира при изменении его размеров.
203+
- [ ] Оптимизация функций случайного заполнения и полной очистки мира.
205204

206205
## От автора
207206

game/GPUAutomaton.cpp

+113-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
GPUAutomaton::GPUAutomaton(int width, int height)
44
: gridWidth(width), gridHeight(height), bufferIndex(0) {
55
CreateComputeShader();
6+
LoadClearShader();
7+
LoadRandomizeShader();
68
SetupBuffers();
79
}
810

@@ -103,6 +105,82 @@ void main() {
103105

104106
}
105107

108+
void GPUAutomaton::LoadClearShader() {
109+
const char* clearShaderSource = R"(
110+
#version 430 core
111+
layout(local_size_x = 32, local_size_y = 32) in;
112+
113+
layout(std430, binding = 0) buffer CellBuffer {
114+
int cells[];
115+
};
116+
117+
layout(std430, binding = 1) buffer ColorBuffer {
118+
vec4 colors[];
119+
};
120+
121+
uniform ivec2 gridSize;
122+
123+
void main() {
124+
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
125+
if (pos.x >= gridSize.x || pos.y >= gridSize.y) return;
126+
127+
uint index = pos.y * gridSize.x + pos.x;
128+
cells[index] = 0; // Мертвая клетка
129+
colors[index] = vec4(0.0, 0.0, 0.0, 1.0); // Черный цвет
130+
}
131+
)";
132+
133+
shaderManager.loadComputeShader("clearShader", clearShaderSource);
134+
shaderManager.linkComputeProgram("clearProgram", "clearShader");
135+
clearProgram = shaderManager.getProgram("clearProgram");
136+
}
137+
138+
void GPUAutomaton::LoadRandomizeShader() {
139+
const char* randomizeShaderSource = R"(
140+
#version 430 core
141+
layout(local_size_x = 32, local_size_y = 32) in;
142+
143+
layout(std430, binding = 0) buffer CellBuffer {
144+
int cells[];
145+
};
146+
147+
layout(std430, binding = 1) buffer ColorBuffer {
148+
vec4 colors[];
149+
};
150+
151+
uniform ivec2 gridSize;
152+
uniform float density;
153+
uniform unsigned int seed;
154+
155+
unsigned int pcg_hash(unsigned int input) {
156+
unsigned int state = input * 747796405u + 2891336453u; // Используйте 'u' для литералов беззнаковых чисел
157+
unsigned int word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
158+
return (word >> 22u) ^ word;
159+
}
160+
161+
void main() {
162+
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
163+
if (pos.x >= gridSize.x || pos.y >= gridSize.y) return;
164+
165+
uint index = pos.y * gridSize.x + pos.x;
166+
uint random = pcg_hash(index + seed);
167+
float randomFloat = float(random & 0x00FFFFFF) / float(0x01000000); // Преобразование в float [0, 1)
168+
169+
if (randomFloat < density) {
170+
cells[index] = 1; // Живая клетка
171+
colors[index] = vec4(0.0, 0.6, 0.0, 1.0); // Зеленый цвет
172+
} else {
173+
cells[index] = 0; // Мертвая клетка
174+
colors[index] = vec4(0.0, 0.0, 0.0, 1.0); // Черный цвет
175+
}
176+
}
177+
)";
178+
179+
shaderManager.loadComputeShader("randomizeShader", randomizeShaderSource);
180+
shaderManager.linkComputeProgram("randomizeProgram", "randomizeShader");
181+
randomizeProgram = shaderManager.getProgram("randomizeProgram");
182+
}
183+
106184
void GPUAutomaton::SetupBuffers() {
107185
GL_CHECK(glGenBuffers(2, cellsBuffer));
108186
GL_CHECK(glGenBuffers(1, &colorsBuffer));
@@ -145,29 +223,56 @@ void GPUAutomaton::Update() {
145223

146224
SwapBuffers();
147225
}
148-
// Как это работает
149-
// На каждом шаге Update() :
150-
// cellsBuffer[currentBufferIndex] привязывается к binding = 0 как входной буфер(current).
151-
// cellsBuffer[(currentBufferIndex + 1) % 2] привязывается к binding = 1 как выходной буфер(next).
152-
// Шейдер выполняет вычисления, записывая новое состояние в next и цвета в colorsBuffer.
153-
// После выполнения SwapBuffers() переключает currentBufferIndex, делая next новым current для следующего шага.
226+
227+
/**
228+
* Как это работает
229+
* На каждом шаге Update() :
230+
* cellsBuffer[currentBufferIndex] привязывается к binding = 0 как входной буфер(current).
231+
* cellsBuffer[(currentBufferIndex + 1) % 2] привязывается к binding = 1 как выходной буфер(next).
232+
* Шейдер выполняет вычисления, записывая новое состояние в next и цвета в colorsBuffer.
233+
* После выполнения SwapBuffers() переключает currentBufferIndex, делая next новым current для следующего шага
234+
*/
154235
void GPUAutomaton::SwapBuffers() {
155236
currentBufferIndex = (currentBufferIndex + 1) % 2;
156237
}
157238

158239
void GPUAutomaton::SetGridState(const std::vector<int>& inState) {
159-
//glFinish();
160240
GL_CHECK(glBindBuffer(GL_SHADER_STORAGE_BUFFER, cellsBuffer[currentBufferIndex]));
161241
GL_CHECK(glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int) * gridWidth * gridHeight, inState.data()));
162242
}
163243

164244
void GPUAutomaton::GetGridState(std::vector<int>& outState) {
165245
outState.resize(gridWidth * gridHeight);
166-
//glFinish();
167246
GL_CHECK(glBindBuffer(GL_SHADER_STORAGE_BUFFER, cellsBuffer[currentBufferIndex]));
168247
GL_CHECK(glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(int) * gridWidth * gridHeight, outState.data()));
169248
}
170249

250+
void GPUAutomaton::ClearGrid() {
251+
glUseProgram(clearProgram);
252+
253+
glUniform2i(glGetUniformLocation(clearProgram, "gridSize"), gridWidth, gridHeight);
254+
255+
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, cellsBuffer[currentBufferIndex]);
256+
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, colorsBuffer);
257+
258+
glDispatchCompute((gridWidth + 31) / 32, (gridHeight + 31) / 32, 1);
259+
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
260+
}
261+
262+
void GPUAutomaton::RandomizeGrid(float density, unsigned int seed) {
263+
glUseProgram(randomizeProgram);
264+
265+
glUniform2i(glGetUniformLocation(randomizeProgram, "gridSize"), gridWidth, gridHeight);
266+
glUniform1f(glGetUniformLocation(randomizeProgram, "density"), density);
267+
glUniform1ui(glGetUniformLocation(randomizeProgram, "seed"), seed);
268+
269+
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, cellsBuffer[currentBufferIndex]);
270+
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, colorsBuffer);
271+
272+
glDispatchCompute((gridWidth + 31) / 32, (gridHeight + 31) / 32, 1);
273+
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
274+
}
275+
171276
void GPUAutomaton::SetCellState(int x, int y, int state) {
172277
if (x < 0 || x >= gridWidth || y < 0 || y >= gridHeight) {
173278
std::cerr << "SetCellState: Invalid coordinates (" << x << ", " << y << ")" << std::endl;

game/GPUAutomaton.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ class GPUAutomaton {
4545
void SetColorsBuf(const std::vector<float>& colors);
4646
void GetColorsBuf(std::vector<float>& colors);
4747

48-
48+
void ClearGrid(); // метод для очистки
49+
void RandomizeGrid(float density, unsigned int seed); // метод для рандомизации
4950
private:
5051
void CreateComputeShader();
5152
void SetupBuffers();
52-
void SwapBuffers(); // Новый метод для переключения буферов
53+
void SwapBuffers(); // метод для переключения буферов
5354

5455
ShaderManager shaderManager;
5556
GLuint computeProgram;
@@ -61,6 +62,11 @@ class GPUAutomaton {
6162

6263
int gridWidth, gridHeight;
6364

65+
GLuint clearProgram; // свойство для программы очистки
66+
void LoadClearShader(); // приватный метод для загрузки шейдера очистки
67+
68+
GLuint randomizeProgram; // свойство для программы рандомизации
69+
void LoadRandomizeShader(); // метод для загрузки шейдера рандомизации
6470
};
6571

6672
#endif // GPU_AUTOMATON_H

game/GameController.cpp

+26-42
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,6 @@ GameController::GameController(int width, int height, float cellSize)
1515

1616
}
1717

18-
void GameController::randomizeGrid() {
19-
if (isRunning) return;
20-
std::random_device rd; // Только для инициализации генератора
21-
std::mt19937 gen(rd()); // Стандартный мерсенновский твистер
22-
std::uniform_int_distribution<> disGliders(3, 15); // Генерация количества глайдеров
23-
std::uniform_int_distribution<> disX(0, grid.getWidth() - 3); // Генерация X-координаты
24-
std::uniform_int_distribution<> disY(0, grid.getHeight() - 3); // Генерация Y-координаты
25-
std::uniform_int_distribution<> disTipe(0, 3); // Генерация чисел от 0 до 3
26-
27-
int numberOfGliders = disGliders(gen); // Случайное количество глайдеров
28-
29-
for (auto i = 0; i < numberOfGliders; ++i) {
30-
int startX = disX(gen);
31-
int startY = disY(gen);
32-
// Размещаем фигуру на сетке в случайном месте
33-
placePattern(startX, startY, gosperGliderGun);
34-
}
35-
}
36-
3718
void GameController::placePattern(int startX, int startY, const Pattern& pattern) {
3819
if (isRunning) return;
3920
int patternHeight = static_cast<int>(pattern.size());
@@ -90,33 +71,36 @@ void GameController::setWoldToroidal(bool wt)
9071
void GameController::randomizeGrid(float density) {
9172
if (isRunning) return;
9273
// Генератор случайных чисел
93-
std::random_device rd; // Только для инициализации генератора
94-
std::mt19937 gen(rd()); // Стандартный мерсенновский твистер
95-
std::uniform_real_distribution<> dis(0.0, 1.0); // Равномерное распределение
96-
97-
for (int y = 0; y < grid.getHeight(); ++y) {
98-
for (int x = 0; x < grid.getWidth(); ++x) {
99-
// Если случайное число меньше density, клетка становится живой
100-
if (dis(gen) < density) {
101-
grid.setCellState(x, y, true);
102-
grid.setCellColor(x, y, 0.0f, 0.6f, 0.0f);
103-
}
104-
else {
105-
grid.setCellState(x, y, false);
106-
grid.setCellColor(x, y, 0.0f, 0.0f, 0.0f);
107-
}
108-
}
109-
}
74+
//std::random_device rd; // Только для инициализации генератора
75+
//std::mt19937 gen(rd()); // Стандартный мерсенновский твистер
76+
//std::uniform_real_distribution<> dis(0.0, 1.0); // Равномерное распределение
77+
78+
//for (int y = 0; y < grid.getHeight(); ++y) {
79+
// for (int x = 0; x < grid.getWidth(); ++x) {
80+
// // Если случайное число меньше density, клетка становится живой
81+
// if (dis(gen) < density) {
82+
// grid.setCellState(x, y, true);
83+
// grid.setCellColor(x, y, 0.0f, 0.6f, 0.0f);
84+
// }
85+
// else {
86+
// grid.setCellState(x, y, false);
87+
// grid.setCellColor(x, y, 0.0f, 0.0f, 0.0f);
88+
// }
89+
// }
90+
//}
91+
92+
// Генерация случайного seed
93+
std::random_device rd;
94+
std::mt19937 gen(rd());
95+
unsigned int seed = gen(); // Генерируем случайное число для seed
96+
97+
// Вызов метода RandomizeGrid с density и seed
98+
gpuAutomaton.RandomizeGrid(density, seed);
11099
}
111100

112101
void GameController::clearGrid() {
113102
if (isRunning) return;
114-
for (int y = 0; y < grid.getHeight(); ++y) {
115-
for (int x = 0; x < grid.getWidth(); ++x) {
116-
grid.setCellState(x, y, false); // Устанавливаем каждую клетку в мертвое состояние
117-
grid.setCellColor(x, y, 0.0f, 0.0f, 0.0f);
118-
}
119-
}
103+
gpuAutomaton.ClearGrid();
120104
}
121105

122106
void GameController::update(float deltaTime) {

game/GameController.h

-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ class GameController {
106106
GameController(int width, int height, float cellSize = 0.5f);
107107

108108
void randomizeGrid(float density);
109-
void randomizeGrid();
110109

111110
void placePattern(int startX, int startY, const Pattern& pattern);
112111
void PlacePattern(int startX, int startY);

rendering/UIRenderer.cpp

-15
Original file line numberDiff line numberDiff line change
@@ -112,21 +112,6 @@ void UIRenderer::DrawSimulationWindow() {
112112
}
113113
}
114114

115-
//if (gameController->getGpuSimulated()) {
116-
// if (ImGui::Button("Симуляция на CPU", buttonSize)) {
117-
// if (!gameController->isSimulationRunning()) {
118-
// gameController->setGpuSimulated(false); // установить симуляцию через ЦП
119-
// }
120-
// }
121-
//}
122-
//else {
123-
// if (ImGui::Button("Симуляция на GPU", buttonSize)) {
124-
// if (!gameController->isSimulationRunning()) {
125-
// gameController->setGpuSimulated(true); // установить симуляцию через видеокарту
126-
// }
127-
// }
128-
//}
129-
130115
if (ImGui::Button("Шаг симуляции", buttonSize)) {
131116
gameController->stepSimulation();
132117
}

system/GLFunctions.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ PFNGLGETINTEGERI_VPROC glGetIntegeri_v = nullptr;
5656
PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = nullptr;
5757

5858
PFNGLISBUFFERPROC glIsBuffer = nullptr;
59+
PFNGLUNIFORM1UIPROC glUniform1ui = nullptr;
5960

6061
void LoadOpenGLFunctions() {
6162
// Загрузка функций для работы с буферами
@@ -169,6 +170,9 @@ void LoadOpenGLFunctions() {
169170

170171
glIsBuffer = (PFNGLISBUFFERPROC)wglGetProcAddress("glIsBuffer");
171172
CHECK_LOAD_FUNCTION(glIsBuffer);
173+
174+
glUniform1ui = (PFNGLUNIFORM1UIPROC)wglGetProcAddress("glUniform1ui");
175+
CHECK_LOAD_FUNCTION(glUniform1ui);
172176

173177

174178

system/GLFunctions.h

+3
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,8 @@ typedef void (APIENTRY* PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pna
449449

450450
typedef GLboolean(APIENTRY* PFNGLISBUFFERPROC)(GLuint buffer);
451451

452+
typedef void (APIENTRY* PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0);
453+
452454
// Объявление указателей на функции
453455
extern PFNGLGENBUFFERSPROC glGenBuffers;
454456
extern PFNGLBINDBUFFERPROC glBindBuffer;
@@ -505,6 +507,7 @@ extern PFNGLGETINTEGERI_VPROC glGetIntegeri_v;
505507
extern PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv;
506508

507509
extern PFNGLISBUFFERPROC glIsBuffer;
510+
extern PFNGLUNIFORM1UIPROC glUniform1ui;
508511

509512
// Функции для работы с OpenGL
510513
void LoadOpenGLFunctions();

windowing/WindowController.cpp

-5
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,6 @@ void WindowController::HandleEvent(UINT message, WPARAM wParam, LPARAM lParam) {
171171
pGameController->setWoldToroidal(!pGameController->getWoldToroidal()); // сделать мир безграничным или на оборот ограничеть его
172172
}
173173
break;
174-
case 'I':
175-
if (!pGameController->isSimulationRunning()) {
176-
pGameController->randomizeGrid(); // засеять поле рандомными фигурами
177-
}
178-
break;
179174
case 'G':
180175
pGameController->setShowGrid(!pGameController->getShowGrid());
181176
break;

0 commit comments

Comments
 (0)