Алгоритм распространения света
Алгоритм освещения на двумерной карте клеток. Распространение света. Плавные тени. Слабые тени.
Родилось из обсуждения с Death и просмотра информации об освещении в майнкрафте, где для распространения света использовалось сложное окто-дерево, FIFO очередь, потом отдельно считали максимальное значение света, причём так криво, что у меня кровь из глаз потекла.
Родилось из обсуждения с Death и просмотра информации об освещении в майнкрафте, где для распространения света использовалось сложное окто-дерево, FIFO очередь, потом отдельно считали максимальное значение света, причём так криво, что у меня кровь из глаз потекла.
- //Вычисляет значение света в точке (x,y) на двумерной карте в зависимости от соседних блоков
- //Возвращает float [-∞ ... 1]
- // -∞ - min
- //ВНИМАНИЕ!!!
- //Отрицательное значение света нужно для выхода из рекурсии!!!
- // 1 - max
- public float getLight(int x, int y){
- //Кэшируем значение поглощения света текущего блока
- // float [0 ... 1] 0 - min, 1 - max
- float ABSORT=getABSORT(x,y);
- //Ищем максимальное значение света у
- //диагональных блоков
- float f1 =Math.max(
- Math.max(getLP(x-1,y-1),getLP(x+1,y-1)),
- Math.max(getLP(x+1,y+1),getLP(x-1,y+1))
- );
- //Ищем максимальное значение света у смежных блоков
- float f2 = Math.max(
- Math.max(getLP(x,y-1),getLP(x+1,y)),
- Math.max(getLP(x,y+1),getLP(x-1, y))
- );
- //Если боковое освещение лучше чем диагональное, то расчитываем свет по смежному
- if(f2>=f1){
- f1=f2-ABSORT;//для бокового освещения
- } else {
- f1=f1-ABSORT*Math.sqrt(2);//для смежного, корень из двух
- //Можно корень вычислить заранее, подставив константу
- }
- return f1;
- }
- //В точке x,y источник света, и для этого источника обновляет
- // Рекурсивный алгоритм обрабатывает только клетки с нулевым значением света
- public void updateLight(x,y){
- float light = getLight(x,y);
- //Если освещение для клетки отрицательно, выходим из рекурсии
- if(light<0) return;
- //Если вычисленный свет меньше или равен, чем уже есть, выходим из рекурсии
- //Таким образом свет от нескольких источников не складывается.
- //Таким образом рекурсия уже не уходит на ранее вычисленные значения.
- //Не нужна FIFO очередь и прочие навороты.
- if(light<=getLP) return;
- setLP(x,y, light);
- updateLight(x,y-1);
- updateLight(x+1,y);
- updateLight(x,y+1);
- updateLight(x-1,y);
- }//Надеюсь, что верно составил рекурсию.
- int w, h;
- //Массив клеток карты
- Cell[] cells =new Cell[w*h];
- public float getLP(int x, int y){
- return cells[x+y*w].LIGHT;
- }
- public void setLP(int x, int y, float l){
- cells[x+y*w].LIGHT=l;
- }
- public float getABSORT(int x, int y){
- return cells[x+y*w].ABSORT;
- }
- public class Cell{
- public float LIGHT=0.0f;
- public float GLIHGT=1.0f;//Это уже глобальное освещение, расчитывается отдельно.
- public float ABSORT=0.1f;
- }