2016-11-14 4 views
10

hereのように、フラグメントシェーダでOren-Nayarライティングを実装しようとしています。OpenGLのOren-Nayarライティング(フラグメントシェーダのビュー方向の計算方法)

しかし、以下に示すように地形には奇妙な照明効果があります。

私は現在、シェーダーにカメラの「フロント」ベクトルとして「ビュー方向」の制服を送ります。カメラを動かすとアーティファクトが変わるので、これが正しいかどうかはわかりません。

'front'ベクトルにMVP行列を乗算するとより良い結果が得られますが、アーチファクトはある角度から地形を見るときにはなお目立ちます。特に、暗い部分や画面の端に目立つことが知られています。

この影響を引き起こす原因は何ですか?

アーティファクトの例のシーンがどのように見えるべきか

enter image description here

enter image description here

バーテックスシェーダ

#version 450 

layout(location = 0) in vec3 position; 
layout(location = 1) in vec3 normal; 

out VS_OUT { 
    vec3 normal; 
} vert_out; 

void main() { 
    vert_out.normal = normal; 
    gl_Position = vec4(position, 1.0); 
} 

テッセレーションコントロールシェーダ

#version 450 

layout(vertices = 3) out; 

in VS_OUT { 
    vec3 normal; 
} tesc_in[]; 

out TESC_OUT { 
    vec3 normal; 
} tesc_out[]; 

void main() { 
    if(gl_InvocationID == 0) { 
     gl_TessLevelInner[0] = 1.0; 
     gl_TessLevelInner[1] = 1.0; 

     gl_TessLevelOuter[0] = 1.0; 
     gl_TessLevelOuter[1] = 1.0; 
     gl_TessLevelOuter[2] = 1.0; 
     gl_TessLevelOuter[3] = 1.0; 
    } 

    tesc_out[gl_InvocationID].normal = tesc_in[gl_InvocationID].normal; 
    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 
} 

テッセレーション評価シェーダ

#version 450 

layout(triangles, equal_spacing) in; 

in TESC_OUT { 
    vec3 normal; 
} tesc_in[]; 

out TESE_OUT { 
    vec3 normal; 
    float height; 
    vec4 shadow_position; 
} tesc_out; 

uniform mat4 model_view; 
uniform mat4 model_view_perspective; 
uniform mat3 normal_matrix; 
uniform mat4 depth_matrix; 

vec3 lerp(vec3 v0, vec3 v1, vec3 v2) { 
    return (
     (vec3(gl_TessCoord.x) * v0) + 
     (vec3(gl_TessCoord.y) * v1) + 
     (vec3(gl_TessCoord.z) * v2) 
    ); 
} 

vec4 lerp(vec4 v0, vec4 v1, vec4 v2) { 
    return (
     (vec4(gl_TessCoord.x) * v0) + 
     (vec4(gl_TessCoord.y) * v1) + 
     (vec4(gl_TessCoord.z) * v2) 
    ); 
} 

void main() { 
    gl_Position = lerp(
     gl_in[0].gl_Position, 
     gl_in[1].gl_Position, 
     gl_in[2].gl_Position 
    ); 

    tesc_out.normal = normal_matrix * lerp(
     tesc_in[0].normal, 
     tesc_in[1].normal, 
     tesc_in[2].normal 
    ); 

    tesc_out.height = gl_Position.y; 

    tesc_out.shadow_position = depth_matrix * gl_Position; 
    gl_Position = model_view_perspective * gl_Position; 
} 

フラグメントシェーダ

#version 450 

in TESE_OUT { 
    vec3 normal; 
    float height; 
    vec4 shadow_position; 
} frag_in; 

out vec4 colour; 

uniform vec3 view_direction; 
uniform vec3 light_position; 

#define PI 3.141592653589793 

void main() { 
    const vec3 ambient = vec3(0.1, 0.1, 0.1); 
    const float roughness = 0.8; 

    const vec4 water = vec4(0.0, 0.0, 0.8, 1.0); 
    const vec4 sand = vec4(0.93, 0.87, 0.51, 1.0); 
    const vec4 grass = vec4(0.0, 0.8, 0.0, 1.0); 
    const vec4 ground = vec4(0.49, 0.27, 0.08, 1.0); 
    const vec4 snow = vec4(0.9, 0.9, 0.9, 1.0); 

    if(frag_in.height == 0.0) { 
     colour = water; 
    } else if(frag_in.height < 0.2) { 
     colour = sand; 
    } else if(frag_in.height < 0.575) { 
     colour = grass; 
    } else if(frag_in.height < 0.8) { 
     colour = ground; 
    } else { 
     colour = snow; 
    } 

    vec3 normal = normalize(frag_in.normal); 
    vec3 view_dir = normalize(view_direction); 
    vec3 light_dir = normalize(light_position); 

    float NdotL = dot(normal, light_dir); 
    float NdotV = dot(normal, view_dir); 

    float angleVN = acos(NdotV); 
    float angleLN = acos(NdotL); 

    float alpha = max(angleVN, angleLN); 
    float beta = min(angleVN, angleLN); 
    float gamma = dot(view_dir - normal * dot(view_dir, normal), light_dir - normal * dot(light_dir, normal)); 

    float roughnessSquared = roughness * roughness; 
    float roughnessSquared9 = (roughnessSquared/(roughnessSquared + 0.09)); 

    // calculate C1, C2 and C3 
    float C1 = 1.0 - 0.5 * (roughnessSquared/(roughnessSquared + 0.33)); 
    float C2 = 0.45 * roughnessSquared9; 

    if(gamma >= 0.0) { 
     C2 *= sin(alpha); 
    } else { 
     C2 *= (sin(alpha) - pow((2.0 * beta)/PI, 3.0)); 
    } 

    float powValue = (4.0 * alpha * beta)/(PI * PI); 
    float C3 = 0.125 * roughnessSquared9 * powValue * powValue; 

    // now calculate both main parts of the formula 
    float A = gamma * C2 * tan(beta); 
    float B = (1.0 - abs(gamma)) * C3 * tan((alpha + beta)/2.0); 

    // put it all together 
    float L1 = max(0.0, NdotL) * (C1 + A + B); 

    // also calculate interreflection 
    float twoBetaPi = 2.0 * beta/PI; 

    float L2 = 0.17 * max(0.0, NdotL) * (roughnessSquared/(roughnessSquared + 0.13)) * (1.0 - gamma * twoBetaPi * twoBetaPi); 

    colour = vec4(colour.xyz * (L1 + L2), 1.0); 
} 
+0

Oren-Nayarのコストが高すぎる場合は、[ラップライティング](http://http.developer.nvidia.com/GPUGems/gpugems_ch16.html)の使用を検討してください。 – BeyelerStudios

答えて

4

モミ私のビュー/ノーマル/ライトベクトルでレンダラにフラグメントシェーダを差し込みました。完全に動作します。だから問題はそれらのベクトルを計算する方法にある必要があります。

次に、view_dirをカメラのフロントベクトルに設定するとします。私は、あなたが「世界の空間におけるカメラの前のベクトル」を意味すると誤解していると思います。カメラ空間内のベクトルでドット積を計算するので、view_dirもカメラ空間内になければなりません。つまり、vec3(0,0,1)はそれを確認する簡単な方法です。それが動作すれば - 私たちはあなたの問題を発見しました。

ただし、パースペクティブ投影を行うと、ビュー方向には正確には正しくありません。フラグメントとカメラの方向は、画面上のフラグメントの位置によって決まるためです。正しい式はview_dir = normalize(-pos)となります。ここで、posは、カメラ空間内のフラグメントの位置です(つまり、投影なしに適用されたモデルビューの行列を使用しています)。あなたのようにそれを計算することができますので、さらに、この量は現在、唯一の画面上のフラグメントの場所によって異なります。

view_dir = normalize(vec3(-(gl_FragCoord.xy - frame_size/2)/(frame_width/2), flen)) 

flenあなたはflen = cot(fovx/2)として計算することができ、お使いのカメラの焦点距離です。

+0

お返事ありがとうございます、私はまもなくこれを見ていきます。 – Caw

+0

答えの一番下に示したように、フラグメントシェーダでview_dirを計算しようとしました。 flenとは何ですか?私はflenとframe_sizeのウィンドウサイズの値を1.0に設定し、影付きの領域でアーティファクトを引き続き見ることができます。地形の下側は、光がその上にある場合は特に悪く見えます。何か案は?助けてくれてありがとう。 – Caw

+0

私は数式が正しくありませんでした。 'flen'に関しても編集を参照してください。私はあなたが "下側"に何を見ているのかわかりませんが、その反対側をレンダリングするときにあなたはおそらくあなたの正常を逆転すべきです。しかし、なぜあなたは地形の "下側"を気にしますか?それを取り除くべきではないか? – ybungalobill