2011-09-09 16 views
3

私は、「シングパスワイヤフレームレンダリング」というペーパーを実装しようとしていますが、これはかなりシンプルなようですが、 。OpenGL:「シングルパスワイヤフレームレンダリング」のデバッグ

紙には標高を把握するための正確なコードはありませんでした。コードは3つの頂点をビューポート空間に投影し、それらの "高度"を取得してフラグメントシェーダに送ります。

フラグメントシェーダは、最も近いエッジの距離を決定し、エッジ強度を生成します。私はこの値で何をすべきかはわかりませんが、[0,1]の間でスケールすることになっているので、私は出力カラーに対して逆数を掛けますが、それはちょうど非常に弱いです。

私はいくつか質問を受けましたが、私は論文で取り上げられているかどうかはわかりません。まず、3Dの代わりに2Dで高度を計算する必要がありますか?次に、彼らはDirectXの機能を提供していますが、DirectXは異なるビューポートスペースのz範囲を持っていますか?それは重要ですか?私は、遠方の高度距離をビューポート空間座標のw値で事前計算しているので、遠近投影法を修正することを推奨します。

image trying to correct for perspective projection

no correction (not premultiplying by w-value)

非補正された画像は、より離れた向きの側面に視点を補正していない明確な問題を抱えているようだが、視点補正された一つが非常に弱いの値を持っています。

誰でも自分のコードに間違いがあるのか​​、ここからデバッグする方法を知ることができますか?

GLSLの私の頂点コード...

float altitude(in vec3 a, in vec3 b, in vec3 c) { // for an ABC triangle 
    vec3 ba = a - b; 
    vec3 bc = c - b; 
    vec3 ba_onto_bc = dot(ba,bc) * bc; 
    return(length(ba - ba_onto_bc)); 
} 

in vec3 vertex; // incoming vertex 
in vec3 v2; // first neighbor (CCW) 
in vec3 v3; // second neighbor (CCW) 
in vec4 color; 
in vec3 normal; 
varying vec3 worldPos; 
varying vec3 worldNormal; 
varying vec3 altitudes; 
uniform mat4 objToWorld; 
uniform mat4 cameraPV; 
uniform mat4 normalToWorld; 
void main() { 
    worldPos = (objToWorld * vec4(vertex,1.0)).xyz; 
    worldNormal = (normalToWorld * vec4(normal,1.0)).xyz; 
    //worldNormal = normal; 
    gl_Position = cameraPV * objToWorld * vec4(vertex,1.0); 
    // also put the neighboring polygons in viewport space 
    vec4 vv1 = gl_Position; 
    vec4 vv2 = cameraPV * objToWorld * vec4(v2,1.0); 
    vec4 vv3 = cameraPV * objToWorld * vec4(v3,1.0); 
    altitudes = vec3(vv1.w * altitude(vv1.xyz,vv2.xyz,vv3.xyz), 
        vv2.w * altitude(vv2.xyz,vv3.xyz,vv1.xyz), 
        vv3.w * altitude(vv3.xyz,vv1.xyz,vv2.xyz)); 
    gl_FrontColor = color; 
} 

と私のフラグメントのコード...

varying vec3 worldPos; 
varying vec3 worldNormal; 
varying vec3 altitudes; 
uniform vec3 cameraPos; 
uniform vec3 lightDir; 
uniform vec4 singleColor; 
uniform float isSingleColor; 
void main() { 
    // determine frag distance to closest edge 
    float d = min(min(altitudes.x, altitudes.y), altitudes.z); 
    float edgeIntensity = exp2(-2.0*d*d); 
    vec3 L = lightDir; 
    vec3 V = normalize(cameraPos - worldPos); 
    vec3 N = normalize(worldNormal); 
    vec3 H = normalize(L+V); 
    //vec4 color = singleColor; 
    vec4 color = isSingleColor*singleColor + (1.0-isSingleColor)*gl_Color; 
    //vec4 color = gl_Color; 
    float amb = 0.6; 
    vec4 ambient = color * amb; 
    vec4 diffuse = color * (1.0 - amb) * max(dot(L, N), 0.0); 
    vec4 specular = vec4(0.0); 
    gl_FragColor = (edgeIntensity * vec4(0.0)) + ((1.0-edgeIntensity) * vec4(ambient + diffuse + specular)); 
} 

答えて

3

まず、あなたの関数の高度は()欠陥がある、ba_onto_bcが計算されますbcは単位長ではないので(bcを正規化するか、またはba_onto_bcを長さ2乗のドット(bc、bc)で除算する - 平方根の計算を保存する)。

一定の厚さのエッジが必要な場合は高度を2Dで計算し、パースペクティブなエッジを必要とする場合は3Dで計算します。

重心座標を別々の頂点属性として使用するほうがはるかに簡単です(つまり、三角形の頂点0は(1 0 0)をとり、2番目の頂点(0 1 0)と最後の頂点(0 0) 1))。フラグメントシェーダでは、最小値を計算し、step()またはsmoothstep()を使用してエッジを計算します。

これは、現在の2つの属性の代わりに1つの属性しか必要とせず、頂点シェーダで高さを計算する必要もなくなります(ただし、重心座標をプリスケールして、オフラインで計算する)。また、瞬時に作業する必要がありますので、望ましい動作に到達するには良い出発点になります。

4

私は豚のアイデアを実現し、その結果は完璧で、ここで私のスクリーンショットでいます

enter image description here

struct MYBUFFEREDVERTEX { 
    float x, y, z; 
    float nx, ny, nz; 
    float u, v; 
    float bx, by, bz; 
}; 

const MYBUFFEREDVERTEX g_vertex_buffer_data[] = { 
    -1.0f, -1.0f, 0.0f, 
    0.0f, 0.0f, 1.0f, 
    0.0f, 0.0f, 
    1.0f, 0.0f, 0.0f, 

    1.0f, -1.0f, 0.0f, 
    0.0f, 0.0f, 1.0f, 
    1.0f, 0.0f, 
    0.0f, 1.0f, 0.0f, 

    -1.0f, 1.0f, 0.0f, 
    0.0f, 0.0f, 1.0f, 
    0.0f, 1.0f, 
    0.0f, 0.0f, 1.0f, 

    1.0f, 1.0f, 0.0f, 
    0.0f, 0.0f, 1.0f, 
    1.0f, 1.0f, 
    1.0f, 0.0f, 0.0f, 
}; 

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 

頂点シェーダ:

#ifdef GL_ES 
// Set default precision to medium 
precision mediump int; 
precision mediump float; 
#endif 

uniform mat4 u_mvp_matrix; 
uniform vec3 u_light_direction; 

attribute vec3 a_position; 
attribute vec3 a_normal; 
attribute vec2 a_texcoord; 
attribute vec3 a_barycentric; 

varying vec2 v_texcoord; 
varying float v_light_intensity; 
varying vec3 v_barycentric; 

void main() 
{ 
    // Calculate vertex position in screen space 
    gl_Position = u_mvp_matrix * vec4(a_position, 1.0); 
    // calculate light intensity, range of 0.3 ~ 1.0 
    v_light_intensity = max(dot(u_light_direction, a_normal), 0.3); 
    // Pass texture coordinate to fragment shader 
    v_texcoord = a_texcoord; 
    // Pass bary centric to fragment shader 
    v_barycentric = a_barycentric; 
} 

フラグメントシェーダ:

#ifdef GL_ES 
// Set default precision to medium 
precision mediump int; 
precision mediump float; 
#endif 

uniform sampler2D u_texture; 

varying vec2 v_texcoord; 
varying float v_light_intensity; 
varying vec3 v_barycentric; 

void main() 
{ 
    float min_dist = min(min(v_barycentric.x, v_barycentric.y), v_barycentric.z); 
    float edgeIntensity = 1.0 - step(0.005, min_dist); 
    // Set diffuse color from texture 
    vec4 diffuse = texture2D(u_texture, v_texcoord) * vec4(vec3(v_light_intensity), 1.0); 
    gl_FragColor = edgeIntensity * vec4(0.0, 1.0, 1.0, 1.0) + (1.0 - edgeIntensity) * diffuse; 
} 
+0

'fwidth(min_dist)、min_dist)'またはそれより優れた 'smoothstep(fwidth(min_dist)) 'を使って、' fwidth(min_dist) 'を1ピクセルの厚さとしてエッジからの距離の単位でカウントできます。 2 * fwidth(min_dist)、min_dist) 'となります。これにより、「マジックナンバー」「0.005」が不要になります。 –

関連する問題