OpenGL: отладка однопроходного рендеринга каркаса - PullRequest
3 голосов
/ 09 сентября 2011

Я пытаюсь реализовать документ «Однопроходный рендеринг каркаса», который кажется довольно простым, но он дает мне то, что я ожидал, вплоть до толстых, темных значений.

В статье не указан точный код для определения высоты, поэтому я сделал это так, как считал нужным. Код должен спроецировать три вершины в область просмотра, получить их «высоты» и отправить их в фрагментный шейдер.

Фрагмент шейдера определяет расстояние до ближайшего ребра и генерирует интенсивность ребра. Я не уверен, что я должен делать с этим значением, но так как оно должно масштабироваться между [0,1], я умножаю обратное на мой исходящий цвет, но оно просто очень слабое.

У меня было несколько вопросов, в которых я не уверен, что они рассматриваются в газетах. Во-первых, следует ли рассчитывать высоты в 2D, а не в 3D? Во-вторых, они содержат функции DirectX, где DirectX имеет другой z-диапазон области просмотра, правильно? Это имеет значение? Я предварительно умножаю исходящие высотные расстояния на значение 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));
}

Ответы [ 2 ]

4 голосов
/ 06 марта 2014

Я реализовал идею свиней, и результат идеален, вот мой скриншот:

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;
}
3 голосов
/ 16 января 2012

Во-первых, ваша функция altitude () ошибочна, ba_onto_bc рассчитывается неправильно, потому что bc не является единицей длины (либо нормализуйте bc, либо делите ba_onto_bc на точку (bc, bc), которая является квадратом длины - вы сохраняете вычисление квадратного корня).

Высоты следует рассчитывать в 2D, если вы хотите, чтобы ребра были постоянной толщины, или в 3D, если вы хотите, чтобы ребра были в перспективе.

Было бы намного проще просто использовать барицентрические координаты какотдельный атрибут вершины (т.е. вершина 0 треугольника получит (1 0 0), вторую вершину (0 1 0) и последнюю вершину (0 0 1)).Во фрагментном шейдере вы должны вычислить минимум и использовать step () или smoothstep () для вычисления ребра.

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

...