Shading
5. Shading
5.1 Flat Shading
5.1.1 Z-flatZ-flat определяет цвет по следующему принципу :
color = max_col - (vertex1.z + vertex2.z + vertex3.z) / a,
где
a - число , позволяющее получить среднее значение z в диапазоне от
0..max_col.
5.1.2 Lambert FlatLambert Flat дает хорошие результаты .
Идея в следующем : источник света представляется как вектор . Для
каждого фрейма мы вычисляем нормаль для каждого полигона и вычисляем
косинус угла между нормалью и источником . Используя таблицу к-тов , можно
использовать полученный результат , умножив косинус на 63 для RGB . Если
значение < 0 , полигон невидим .
- - LSi, LSj, LSk = цветовые к-ты
- - Ni, Nj, Nk = к-ты нормали
- function LambertFlat
- < подсчитаем нормальные к-ты >
- // a = |N| * |LS|
- a = sqrt(Ni*Ni + Nj*Nj + Nz*Nz) * sqrt(LSi*LSi + LSj*LSj +
LSk*LSk)
- if a<>0
- color = max_col * (LSi*Ni + LSj*Nj + LSk*Nk) / a
- if color<0
- else
- return color
- endf
5.2 Gouraud Shading
5.2.1 Z-Gouraud
5.2.2 "Real" GouraudРаботает аналогично Lambert Flat, но берется
угол между vertex normal и источником света .
Vertex normals в каждой вершине перпендикулярны к поверхности .
Алгоритм :
- 1. Установим все vertex normals в ноль
- 2. Для каждой грани , вычислим нормаль и прибавим ее кe vertex normal
.
- 3. Нормализуем все vertex normals
Псевдокод :
- 1. Находим все грани , прилегающие к данной вершине .
- 2. Прибавляем нормали этих граней к данной vertex normal
- 3. Делим vertex normal на число прилегающих нормалей
- 4. Нормализуем vertex normals
- function CalcNormals
- < вычисление нормали для плоскости ; >
- function calcnor(X1,Y1,Z1,X2,Y2,Z2,X3,Y3,Z3,NX,NY,NZ)
- int RelX1,RelY1,RelZ1,RelX2,RelY2,RelZ2
- RelX1=X2-X1
- RelY1=Y2-Y1
- RelZ1=Z2-Z1
- RelX2=X3-X1
- RelY2=Y3-Y1
- RelZ2=Z3-Z1
- NX=RelY1*RelZ2-RelZ1*RelY2
- NY=RelZ1*RelX2-RelX1*RelZ2
- NZ=RelX1*RelY2-RelY1*RelX2
- endf
- < face = polygon table, vertex = vertex table >
- int i,a,ox,oy,oz
- float cx,cy,cz,len,cn
- for i=0 -> num_of_vertices-1
- cx=0
- cy=0
- cz=0
- cn=0
- for a=0 -> num_of_faces-1
- < if the face touches the vertex i >
- if ((face[a][0]=i) or (face[a][1]=i) or (face[a][2]=i))
- < the function returns to (ox,oy,oz) the normal vector
>
- calcnor(
- vertex[face[a][0]].x,vertex[face[a][0]].y,
vertex[face[a][0]].z,vertex[face[a][1]].x,
vertex[face[a][1]].y,vertex[face[a][1]].z,
vertex[face[a][2]].x,vertex[face[a][2]].y,
vertex[face[a][2]].z,ox,oy,oz
- )
- < (cx,cy,cz) will carry the average of the plane
normals, cn is incremented because it tells how many normals have
been calculated into c* >
- cx=cx+ox
- cy=cy+oy
- cz=cz+oz
- cn+=1
- endif
- endfor
- < if some polygon touches the vertex >
- if cn > 0
- < calculate the averages >
- cx=cx/cn
- cy=cy/cn
- cz=cz/cn
- < calculate the length of the normal >
- len=sqrt(cx*cx+cy*cy+cz*cz)
- if len = 0
- len=1
- endif
- < normalize the vectors >
- normal[i].x=cx/len
- normal[i].y=cy/len
- normal[i].z=cz/len
- endif
- endfor
- endf
Похоже на Lambert flat.
5.3 Phong Shading
5.3.1 Environment mappingНужно различать real phong и env-mapping
- это 2 разных вещи . При env-mapping мы используем bitmap , здесь можно
получить такой эффект , как металлический оттенок . Env-mapping работает
аналогично gouraud , но вместо gouraud filler используется texture filler,
и вместо углов используются нормальные к-ты x и y .
- - LS = вектор источника света
- - N[0..2] = vertex normals для треугольника
- - cx1, cy1 etc = координаты в env-map
- - env-map = 256x256-битмап
- if ( LS.k <= 0 ) ;
- cx1 = env_crd( N[0].i - LS.i )
- cy1 = env_crd( N[0].j - LS.j )
- cx2 = env_crd( N[1].i - LS.i )
- cy2 = env_crd( N[1].j - LS.j )
- cx3 = env_crd( N[2].i - LS.i )
- cy3 = env_crd( N[2].j - LS.j )
- else
- a = N[0].i + LS.i ;
- if (a<0)
- a = a + 1 ; двигаемся в противоположную сторону
- else
- cx1 = env_crd( a ) ; convert
- a = N[0].j + LS.j
- if (a<0)
- else
- cy1 = env_crd ( a )
- a = N[1].i + LS.i
- if (a<0)
- else
- cx2 = env_crd( a )
- a = N[1].j + LS.j
- if (a<0)
- else
- cy2 = env_crd ( a )
- a = N[2].i + LS.i
- if (a<0)
- else
- cx3 = env_crd( a )
- a = N[2].j + LS.j
- if (a<0)
- else
- cy3 = env_crd ( a )
- endif
- texture( x1, y1, x2, y2, x3, y3, cx1, cy1, cx2, cy2, cx3, cy3
)
- function env_crd ( float value )
- a = value * 127 + 128
- return a
- endf
Функция env_crd() конвертирует нормальный к-т
( -1..1) в координату env-map (0..255) .
Вначале мы проверяем знак к-та z , положительное значение которого
требует bit fixing. При отрицательном значении этого к-та мы подсчитываем
нормальные x и y к-ты .
Эта техника не работает с источником , имеющим положительный к-т
z .
5.3.2 "Real" PhongДля Phong shading необходимы 4 вектора :
- - свет
- - нормаль поверхности
- - камера
- - отраженный вектор
В цикле мы интерполируем 3 вектора , и
яркость может быть найдена :
x
= угол между лучом отраженным и камерой
color = ambient + (cos b) * diffuse + (cos x)^n * specular
5.4 Light Source Handling
5.4.1 Freely moving lightsourcesМы сохраняем только
расположение источника света и подсчитываем вектор от него до вершины ,
которая будет прорисована , и нормализуем ее . Новый вектор света
подсчитан .
5.4.2 Light attenuationНемного базовой математики : подсчитаем
расстояние между каждой вершиной и источником света , и поставим
световую интенсивность в зависимость от этого расстояния :
- ; для каждой вершины в грани
- for a=0 -> num_of_vertices-1
- ; посчитаем новый источник света
- l_vector.x = vertex[a].x_coord - light.x_coord
- l_vector.y = vertex[a].y_coord - light.y_coord
- l_vector.z = vertex[a].z_coord - light.z_coord
- distance = sqrt((l_vector.x)^2 + (l_vector.x)^2 +
- ; нормализуем новый источник света
- l_vector.x = l_vector.x / distance
- l_vector.y = l_vector.y / distance
- l_vector.z = l_vector.z / distance
- ; calculate brightness
- brightness = 1 - (distance/light.fadezedo)^fogness
- light_at_vertex[a] = gouraud(vertex1.normal,brightness)
- endfor
- function gouraud (param normal, brightness)
- color = ( l_vector.x*normal.x + l_vector.y*normal.y +
- l_vector.z*normal.z ) * brightness
- if color<0
- else if color>255
- return color
- endf
Смотрите также: