2008-12-03 5 views
6

Как вы вычисляете угол между двумя нормалями в glsl? Я пытаюсь добавить эффект френеля к внешним краям объекта (объединяя этот эффект с затенением phong), и я думаю, что угол - это единственное, что мне не хватает.Как вы вычисляете угол между двумя нормалями в glsl?

Фрагмент Shader:

varying vec3 N; 
varying vec3 v; 

void main(void) { 
    v = vec3(gl_ModelViewMatrix * gl_Vertex); 
    N = normalize(gl_NormalMatrix * gl_Normal); 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 
} 

Vertex Shader:

varying vec3 N; 
varying vec3 v; 

void main(void) { 
    vec3 L = normalize(gl_LightSource[0].position.xyz - v); 
    vec3 E = normalize(-v); 
    vec3 R = normalize(-reflect(L,N)); 

    vec4 Iamb = gl_FrontLightProduct[0].ambient 
    vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0); 
    vec4 Ispec = gl_FrontLightProduct[0].specular * pow(max(dot(R,E),0.0), gl_FrontMaterial.shininess); 
    vec4 Itot = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec; 

    vec3 A = //calculate the angle between the lighting direction and the normal// 
    float F = 0.33 + 0.67*(1-cos(A))*(1-cos(A))*(1-cos(A))*(1-cos(A))*(1-cos(A)); 
    vec4 white = {1.0, 1.0, 1.0, 1.0}; 

    gl_FragColor = F*white + (1.0-F)*Itot; 
} 

варьируя Vec3

ответ

5

Из скалярного произведения двух векторов, вы можете получить косинус угла между ними

cos A = DotProduct(v1, v2)/(Length(v1) * Length(v2)) 

Используя это, вам не нужно вычислять косинус при вычислении F. Так как ваши векторы являются единичными векторами, например, имеют длину один, вы даже можете избежать деления.

+0

В glsl даже есть встроенный оператор точечного продукта. Строка кода будет красивой: cA = точка (v1, v2); После этого все ссылки cos (A) могут быть заменены на cA. – 2008-12-03 21:58:21

11

dot продукт между двумя векторами вернет косинус угла (в GLSL это точка (a, b)). Взятие дугового косинуса из этого будет возвращать угол в радианах (в GLSL это acos (x)).

Dot-продукт очень дешевый, дуго-косинус довольно дорог.

Однако эффект Френеля действительно не требует угла. Достаточно иметь точечный результат между векторами. Существует много приближений для эффекта Френеля, один из самых дешевых - это просто использование точки непосредственно. Или возведение в квадрат (x * x) или повышение до некоторой степени.

В вашем шейдере выше, похоже, вы просто хотите поднять точку до 5-й степени. Что-то вроде:

float oneMinusDot = 1.0 - dot(L, N); 
float F = pow(oneMinusDot, 5.0);