Почти все статьи и книги, которые я прочитал, утверждают, что композиция окончательного цвета:
finalColor = ambientColor + lambertianTerm * diffuseColor (= цвет материала) + specularIntensity * specularColor (= светлый цвет)
lambertianTerm = dot( surfaceNormal, normalize( lightPos - surfacePos ) );
halfVector = normalize( normalize( lightPos - surfacePos ) + normalize( eyePos - surfacePos );
specularFactor = max( dot( halfVector, surfaceNormal ), 0.0f );
specularIntensity = pow( specularFactor, surfaceShininessLevel );
Вот некоторые из мест, где используется вышеупомянутый подход для вычисления окончательного цвета: OpenGL SuperBible 6th edition -> Rendering Techniques -> Lighting Models -> Blinn-Phong Lighting, Wikipedia -> модель затенения Блинна-Фонга (см. Фрагментный шейдер), и другие.
Существует проблема в расчете конечного цвета: цвет света (также рассмотрим случай с несколькими источниками света) не участвует в формулировке рассеянного цвета. Рассмотрим случай, когда коэффициент блеска велик — скажем, 128,0, так что зеркальное пятно мало и большая часть площади объекта окрашена с использованием диффузного члена. Также позвольте цвету объекта быть зеленым и свету — красным. Результатом вышеприведенного уравнения, если нет окружающего цвета, является частично освещенный чисто зеленый объект с небольшим желтым пятном на нем = зеленый + красный = свет от красного источника света, отраженный от зеленого объекта:
Это не правильно. Конечно, зеленый + красный — желтый, но наверняка вы не увидите ни чисто зеленого шара, ни желтого зеркального пятна. Держите в руке зеленый блестящий шар — например, большую шестерку из бильярдной игры, а затем зажгите его красным светом — я могу заверить вас, что вы не увидите только зеленый рассеянный и желтый зеркальный. Вместо этого вы увидите смешанный зеленый + красный для размытого и более красного зеркального пятна. Лучший способ, который я нашел на данный момент, чтобы вычислить окончательный цвет — среднее смешивание:
finalColor = ambient
+ lambertianTerm * ( surfaceColor + lightColor ) / 2.0
+ specularIntensity * lightColor;
const vec4 srgbLuminanceFactor = vec4( 0.2125f, 0.7154f, 0.0721f, 1.0f );
vec4 overlay( vec4 baseColor, vec4 blendColor )
{
float luminance = dot( baseColor, srgbLuminanceFactor );
vec4 lowerLumOverlay = 2.0f * blendColor * baseColor;
if ( luminance < 0.45f )
{
return lowerLumOverlay;
}
const vec4 whiteColor = vec4( 1.0 );
vec4 higherLumOverlay = whiteColor - 2.0f * ( whiteColor - blendColor ) * ( whiteColor - baseColor );
return luminance > 0.55f
? higherLumOverlay
: mix( lowerLumOverlay, higherLumOverlay, ( luminance - 0.45f ) * 10.0f );
}
… но не выглядит хорошо.
Вероятно, свет и цвет объекта должны быть смешаны в другом линейном соотношении:
mix( surfaceColor, lightColor, ratio );
… или нелинейным способом, о котором я не могу думать.
Итак, последний вопрос: каков лучший расчет полного цвета?
Кроме того, скажите мне, если я что-то упускаю, но, по моему мнению, чисто зеленая картинка + чисто желтая зеркальная подсветка совершенно не сценарий реального мира.
Цветовой смысл поверхности — это «какие цвета света могут быть диффузный с этой поверхности «.
Цветовой смысл означает, что цвета света могут быть отраженный с этой поверхности «.
Светлый цвет означает «что цвета света достигают поверхности».
Таким образом, вы должны умножить их:
finalColor = ambient
+ lambertianTerm * surfaceColor * lightColor
+ specularIntensity * specularColor * lightColor;
Других решений пока нет …