Шейдер GLSL для глянцевых зеркальных отражений на кубической поверхности

Я написал шейдер для экологического cubemapping

* Vertex shader *

varying vec3 Normal;
varying vec3 EyeDir;
uniform samplerCube cubeMap;

void main()
{
        gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;
        Normal = gl_NormalMatrix * gl_Normal;
        EyeDir = vec3(gl_ModelViewMatrix * gl_Vertex);
}

* Фрагментный шейдер *

varying vec3 Normal;
varying vec3 EyeDir;

uniform samplerCube cubeMap;

 void main(void)
 {
    vec3 reflectedDirection = normalize(reflect(EyeDir, normalize(Normal)));
    reflectedDirection.y = -reflectedDirection.y;
    vec4 fragColor = textureCube(cubeMap, reflectedDirection);
    gl_FragColor = fragColor;
}

Это классический результат: http://braintrekking.files.wordpress.com/2012/07/glsl_cubemapreflection.png?w=604&h=466 Теперь я хочу добавить зеркальную белую подсветку, чтобы получить более глянцевый эффект, например, motherpearl. Как можно добавить этот вид подсветки? Как и на этом изображении. Должен ли я суммировать зеркальный компонент на gl_FragColor? Первой попыткой является вычисление зеркального отражения в вершинном шейдере

vec3 s = normalize(vec3(gl_LightSource[0].position - EyeDir));
vec3 v = normalize(EyeDir);
vec3 r = reflect( s, Normal );
vec3 ambient = vec3(gl_LightSource[0].ambient*gl_FrontMaterial.ambient);

float sDotN = max( dot(s,Normal), 0.0 );
vec3 diffuse = vec3(gl_LightSource[0].diffuse * gl_FrontMaterial.diffuse * sDotN);
vec3 spec = vec3(0.0);
if( sDotN > 0.0 )
    spec = gl_LightSource[0].specular * gl_FrontMaterial.specular * pow( max( dot(r,v), 2.0 ), gl_FrontMaterial.shininess );

LightIntensity = 0*ambient + 0*diffuse +  spec;

и умножить его на gl_FragColor, но эффект, который я получаю, не является убедительным.

У кого-то есть идея, как это сделать?

Ответы

Ответ 1

Вот пример того, как вы могли сделать это:

Перламутровый эффект ВЫКЛ:
enter image description here
Перламутровый эффект ВКЛ:
enter image description here

Vertex shader:

uniform vec3 fvEyePosition;

varying vec3 ViewDirection;
varying vec3 Normal;

void main( void )
{
   gl_Position = ftransform();
   vec4 fvObjectPosition = gl_ModelViewMatrix * gl_Vertex;

   ViewDirection  = fvEyePosition - fvObjectPosition.xyz;
   Normal         = gl_NormalMatrix * gl_Normal;
}

Фрагментный шейдер:

uniform samplerCube cubeMap;

varying vec3 ViewDirection;
varying vec3 Normal;

const float mother_pearl_brightness = 1.5;

#define MOTHER_PEARL

void main( void )
{
   vec3  fvNormal         = normalize(Normal);
   vec3  fvViewDirection  = normalize(ViewDirection);
   vec3  fvReflection     = normalize(reflect(fvViewDirection, fvNormal)); 

#ifdef MOTHER_PEARL
   float view_dot_normal = max(dot(fvNormal, fvViewDirection), 0.0);
   float view_dot_normal_inverse = 1.0 - view_dot_normal;

   gl_FragColor = textureCube(cubeMap, fvReflection) * view_dot_normal;
   gl_FragColor.r += mother_pearl_brightness * textureCube(cubeMap, fvReflection + vec3(0.1, 0.0, 0.0) * view_dot_normal_inverse) * (1.0 - view_dot_normal);
   gl_FragColor.g += mother_pearl_brightness * textureCube(cubeMap, fvReflection + vec3(0.0, 0.1, 0.0) * view_dot_normal_inverse) * (1.0 - view_dot_normal);
   gl_FragColor.b += mother_pearl_brightness * textureCube(cubeMap, fvReflection + vec3(0.0, 0.0, 0.1) * view_dot_normal_inverse) * (1.0 - view_dot_normal);
#else
   gl_FragColor = textureCube(cubeMap, fvReflection);
#endif
}

Конечно, способ вычисления компонент R, G и B не очень корректен, но я отправляю этот код, чтобы показать вам путь, а не решение.


EDIT:

Здесь обещанная "правильная" версия переливного шейдера:

Vertex shader:

varying vec3 v_view_direction;
varying vec3 v_normal;
varying vec2 v_texture_coordinate;

void main(void)
{
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   v_texture_coordinate = gl_MultiTexCoord0.xy;
   v_view_direction = -gl_ModelViewMatrix[3].xyz;
   v_normal = gl_NormalMatrix * gl_Normal;
}

Фрагментный шейдер:

uniform samplerCube texture_reflection;
uniform sampler2D texture_iridescence;
uniform sampler2D texture_noise;

varying vec3 v_view_direction;
varying vec3 v_normal;
varying vec2 v_texture_coordinate;

const float noise_strength = 0.5;

void main(void)
{
   vec3 n_normal = normalize(v_normal);
   vec3 n_wiew_direction = normalize(v_view_direction);
   vec3 n_reflection = normalize(reflect(n_wiew_direction, n_normal)); 

   vec3 noise_vector = (texture2D(texture_noise, v_texture_coordinate).xyz - vec3(0.5)) * noise_strength;

   float inverse_dot_view = 1.0 - max(dot(normalize(n_normal + noise_vector), n_wiew_direction), 0.0);
   vec3 lookup_table_color = texture2D(texture_iridescence, vec2(inverse_dot_view, 0.0)).rgb;

   gl_FragColor.rgb = textureCube(texture_reflection, n_reflection).rgb * lookup_table_color * 2.5;
   gl_FragColor.a = 1.0;
}

Результаты

Нет радужного эффекта:
enter image description here

Радужный эффект (текстура поиска 1):
enter image description here

Радужный эффект (текстура поиска 2):
enter image description here

Текстура поиска радужной оболочки 2: enter image description here

Текстура шума:
enter image description here

Примечания:
Текстура поиска радужной оболочки также может быть 1D текстурой, которая будет намного более эффективной с точки зрения памяти.
Кроме того, способ вычисления вектора шума на самом деле вздор. Правильным решением было бы использовать отображение рельефа. Но эй, это работает!: D