2D Bump (Эффект неровной поверхности). 
c Федор Цыплаков


Во многих демо используется эффект 2D bump (2D рельефа), когда источник света (чаще всего окружность) двигается по "рельефному" экрану.

Вот как это делается:

Освещенность точки зависит от угла между нормалью каждого пиксела, и источником света.

Так как вектор нормали имеет три координаты (x,y,z), и мы знаем координаты x и y, то z вычисляется по формуле z=1-sqrt(x*x+y*y). Если непонятно, откуда взялась эта формула -- вам пора вспомнить геометрию ;) 

Так как в реальном времени это будет очень медленно, то создают заранее таблицу предвыборки, где для каждого X и Y вычисляется значение вектора Z. Эта таблица называется enviroment map (карта окружения). Подобные карты используются и при текстурировании "зеркальных" объектов (что намного проще, чем прямая трассировка :) Обычно хватает карты размером 256х256. Генерить ее можно и с помощью 1-sqrt(x^2+y^2), и с помощью синусов. В общем случае можно использовать любую модель освещения - все зависит от того, что вы хотите получить :) .

Вот пример построения простейшей карты:

   int i,j;float x,y;
   int density=128; //размер пятна
   int bright; //количество оттенков

   for (i=0;i<256;i++){
   for (j=0;j<256;j++){
     x=j/density-1;// (делается для того, чтобы прирвести координаты
     y=i/density-1;//  к значениям от -1 до 1)
     z=1-sqrt(x*x+y*y);
     z=z<0?z:0;
     envmap[i][j]=z*bright;}}


Точка на экране для вышеприведенного случая рассчитывается по формуле result=envmap[X нормали+density][Y нормали+density] (так как точка наибольшей освещенности лежит посредине карты окружения) 

Нормали X и Y рассчитываем так: 
                                  xnorm=screen[x,y]-screen[x+n,y],
                                  ynorm=screen[x,y]-screen[x,y+n].
                                  
Где n - число от (-5 до 5,n!=0) Большие числа приводят к сильному ухудшению картинки. (А может вам и не надо "правильное" отображение ? Тогда можно применять и большие числа) Параметр n влияет на "высоту рельефа". Не забудьте, что при отрицательных значениях n рельеф будет "вдавленным", при n>0 - выпуклым.)

Это все касается неподвижного источника с координатами (0,0,-бесконечность) Для того, чтобы пятно света двигалось, нужно всего лишь из получившейся нормали вычесть разность координат пиксела (освещенность которого рассчитывается) и координат источника света. Окончательная освещенность точки получается такая:

   result=envmap[(xnorm-(lx-x))][(ynorm-(lx-y))]


Получившееся значение сравнивается с границами допустимого диапазона яркости, и , если нужно, исправляется в соответствии с этой границей.

Пример. 

lx,ly - координаты источника света.
x,y - координаты точки на экране и на бамп-текстуре.
b_low,b_max - минимальный и максимальный доустимый порог освещенности точки. 
   for (int y=0;y<200;y++){
   for (int x=0;x<320;x++){
   int xn=(bmap[x][y]-bmap[x+1][y])-(lx-x)+density;
   int yn=(bmap[x][y]-bmap[x][y+1])-(ly-y)+density;
   if (xnb_max) xn=b_low;
   if (ynb_max) yn=b_max;
   screen[x][y]=envmap[xn][yn];}}

Вот, собственно, и все. 
