【问题标题】:OpenGL: debugging "Single-pass Wireframe Rendering"OpenGL:调试“单通道线框渲染”
【发布时间】:2011-11-13 18:21:16
【问题描述】:

我正在尝试实现论文“Single-Pass Wireframe Rendering”,这看起来很简单,但它给了我我所期望的厚实、黑暗的价值。

论文没有给出准确的代码来计算海拔高度,所以我按照我认为合适的方式做了。代码应该将三个顶点投影到视口空间,获取它们的“高度”并将它们发送到片段着色器。

片段着色器确定最近边缘的距离并生成一个边缘强度。我不确定我应该用这个值做什么,但由于它应该在 [0,1] 之间缩放,我将逆向乘以我的输出颜色,但它只是非常弱。

我有几个问题不确定是否在论文中得到解决。首先,高度是否应该以 2D 而不是 3D 计算?其次,他们提供 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));
}

【问题讨论】:

    标签: opengl render glsl wireframe


    【解决方案1】:

    我已经实现了swine的想法,结果很完美,这是我的截图:

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

    【讨论】:

    • 你可以使用fwidth(min_dist)作为1像素的厚度,以距离边缘的距离为单位,使用step(fwidth(min_dist), min_dist)或者更好的smoothstep(fwidth(min_dist), 2 * fwidth(min_dist), min_dist)。这消除了对“幻数”0.005 的需要。
    【解决方案2】:

    首先,您的函数高度()有缺陷,ba_onto_bc 计算错误,因为 bc 不是单位长度(要么标准化 bc,要么将 ba_onto_bc 除以长度平方的 dot(bc, bc) - 你省去了计算平方根) .

    如果您想要恒定厚度的边缘,则应以 2D 计算高度,如果您想要透视正确的边缘,则应以 3D 计算。

    将重心坐标用作单独的顶点属性会容易得多(即三角形的顶点 0 将得到 (1 0 0)、第二个顶点 (0 1 0) 和最后一个顶点 (0 0 1 ))。在片段着色器中,您将计算最小值并使用 step() 或 smoothstep() 来计算边缘。

    这将只需要 1 个属性而不是当前的两个属性,并且它还消除了在顶点着色器中计算高度的需要(尽管如果您想预先缩放重心坐标以便拥有均匀粗线,这可能很有用 - 但是离线计算)。它也应该几乎可以立即工作,因此这将是实现所需行为的良好起点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-11
      • 1970-01-01
      • 1970-01-01
      • 2018-01-22
      • 1970-01-01
      • 1970-01-01
      • 2020-02-11
      • 1970-01-01
      相关资源
      最近更新 更多