4

私は論文「Single-Pass Wireframe Rendering」を実装しようとしていますが、これは非常に単純に見えますが、厚くて暗い値に関しては期待どおりの結果が得られます。

この論文には、高度を計算するための正確なコードが記載されていなかったので、適切だと思ったとおりに実行しました。コードは、3 つの頂点をビューポート空間に投影し、それらの「高度」を取得して、フラグメント シェーダーに送信する必要があります。

フラグメント シェーダーは、最も近いエッジの距離を決定し、edgeIntensity を生成します。この値で何をすべきかわかりませんが、[0,1] の間でスケーリングすることになっているため、出力色に対して逆数を掛けますが、非常に弱いだけです。

論文で取り上げられているかどうかわからないいくつかの質問がありました。まず、高度は 3D ではなく 2D で計算する必要がありますか? 2 つ目は、DirectX のビューポート空間の Z 範囲が異なる DirectX の機能をサイトに掲載したことです。それは問題ですか?透視投影を修正することを推奨しているため、ビューポート空間座標の w 値で発信高度距離を事前に乗算しています。

透視投影を修正しようとしている画像

補正なし (事前に w 値を掛けない)

補正されていない画像には、遠方を向いている側の遠近法が補正されていないという明らかな問題があるようですが、遠近法が補正された画像の値は非常に弱いです。

私のコードの何が問題なのか、またはここからデバッグする方法を誰かが見ることができますか?

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));
}
4

2 に答える 2

5

私は豚のアイデアを実装しました。結果は完璧です。これが私のスクリーンショットです:

ここに画像の説明を入力

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;
}
于 2014-03-06T09:52:25.020 に答える
3

まず、関数 Height() に欠陥があります。bc が単位長ではないため、ba_onto_bc が正しく計算されません (bc を正規化するか、長さの 2 乗である dot(bc, bc) で ba_onto_bc を分割します - 平方根の計算を保存します)。

高度は、一定の厚さのエッジが必要な場合は 2D で計算する必要があり、遠近法で正確なエッジが必要な場合は 3D で計算する必要があります。

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

これにより、現在の 2 つの属性ではなく 1 つの属性のみが必要になり、頂点シェーダーで高さを計算する必要もなくなります (ただし、重心座標を事前にスケーリングして線を均一に太くしたい場合は便利ですが、オフラインで計算します)。 )。また、ほぼ瞬時に動作するはずなので、目的の動作に到達するための出発点として適しています。

于 2012-01-15T21:23:34.987 に答える