GLSL如何使用几何着色器显示法线?

时间:2013-08-29 12:13:24

标签: opengl glsl geometry-shader

我有vertex shader

#version 330 core

layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec2 VertexUV;
layout(location = 2) in vec3 VertexNormal;

out VS_GS_VERTEX
{
    vec2 UV;
    vec3 vs_worldpos;
    vec3 vs_normal;
} vertex_out;

uniform mat4 proj_matrix;
uniform mat4 model_matrix;

void main(void)
{
    gl_Normal = VertexNormal;
    gl_Position = proj_matrix * vec4(VertexPosition, 1.0);

    vertex_out.UV = VertexUV; //VertexPosition.xy;
    vertex_out.vs_worldpos = gl_Position.xyz;
    vertex_out.vs_normal = mat3(model_matrix) * gl_Normal;
}

fragment shader

#version 330 core

in GS_FS_VERTEX
{
    vec2 UV;
    vec3 vs_worldpos;
    vec3 vs_normal;
} vertex_in;

// Values that stay constant for the whole mesh.
uniform sampler2D sampler0;
uniform sampler2D sampler1;
uniform sampler2D sampler2;
uniform sampler2D sampler3;
//uniform sampler2D alphamap0;
uniform sampler2D alphamap1;
uniform sampler2D alphamap2;
uniform sampler2D alphamap3;
uniform int tex_count;

uniform vec4 color_ambient = vec4(0.75, 0.75, 0.75, 1.0);
uniform vec4 color_diffuse = vec4(0.25, 0.25, 0.25, 1.0);
//uniform vec4 color_specular = vec4(1.0, 1.0, 1.0, 1.0);
uniform vec4 color_specular = vec4(0.1, 0.1, 0.1, 0.25);
uniform float shininess = 5.0f;
uniform vec3 light_position = vec3(12.0f, 32.0f, 560.0f);

void main(){
    vec3 light_direction = normalize(light_position - vertex_in.vs_worldpos);
    vec3 normal = normalize(vertex_in.vs_normal);
    vec3 half_vector = normalize(light_direction + normalize(vertex_in.vs_worldpos));
    float diffuse = max(0.0, dot(normal, light_direction));
    float specular = pow(max(0.0, dot(vertex_in.vs_normal, half_vector)), shininess);
    gl_FragColor = texture( sampler0, vertex_in.UV ) * color_ambient + diffuse * color_diffuse + specular * color_specular;

    // http://www.opengl.org/wiki/Texture_Combiners
    // GL_MODULATE = *
    // GL_INTERPOLATE Blend tex0 and tex1 based on a blending factor = mix(texel0, texel1, BlendFactor)
    // GL_INTERPOLATE Blend tex0 and tex1 based on alpha of tex0 = mix(texel0, texel1, texel0.a)
    // GL_ADD = clamp(texel0 + texel1, 0.0, 1.0)
    if (tex_count > 0){
        vec4 temp = texture( sampler1, vertex_in.UV );
        vec4 amap = texture( alphamap1, vertex_in.UV);
        gl_FragColor = mix(gl_FragColor, temp, amap.a);
    }
    if (tex_count > 1){
        vec4 temp = texture( sampler2, vertex_in.UV );
        vec4 amap = texture( alphamap2, vertex_in.UV);
        gl_FragColor = mix(gl_FragColor, temp, amap.a);
    }
    if (tex_count > 2){
        vec4 temp = texture( sampler3, vertex_in.UV );
        vec4 amap = texture( alphamap3, vertex_in.UV);
        gl_FragColor = mix(gl_FragColor, temp, amap.a);
    }
}

将索引GL_TRIANGLE_STRIP作为输入

glBindBuffer(GL_ARRAY_BUFFER, tMt.vertex_buf_id[cx, cy]);
glVertexAttribPointer(VERTEX_LAYOUT_POSITION, 3, GL_FLOAT, false, 0, pointer(0));
glEnableVertexAttribArray(0);

{ chunk tex position }
glBindBuffer(GL_ARRAY_BUFFER, chunkTexPositionBO);
glVertexAttribPointer(VERTEX_LAYOUT_TEX_UV, 2, GL_FLOAT, false, 0, pointer(0));
glEnableVertexAttribArray(1);

glBindBuffer(GL_ARRAY_BUFFER, tMt.normal_buf_id[cx, cy]);
glVertexAttribPointer(VERTEX_LAYOUT_NORMAL, 3, GL_FLOAT, true, 0, pointer(0));
glEnableVertexAttribArray(2);

{ index buffer }
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, chunkIndexBO);

for i := 0 to tMt.texCount - 1 do begin
  bt := tMt.texture_buf_id[cx, cy][i];
  if bt = nil then
     break;
  glUniform1i(proj_tex_count_loc, i);
  glActiveTexture(GL_TEXTURE0 + i);
  glBindTexture(GL_TEXTURE_2D, bt.id);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  if i > 0 then begin
    // this time, use blending:
    glActiveTexture(GL_TEXTURE4 + 1);
    glBindTexture(GL_TEXTURE_2D, tMt.alphamaps[cx, cy][i - 1]);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  end;
end;

glDrawElements(GL_TRIANGLE_STRIP, length(chunkIndexArr), GL_UNSIGNED_SHORT, nil);

代码按预期工作,但我不确定我的法线是否正确排列:它们存储为字节(转换为GLfloat为b / FF),坐标xyz已更改,有些可能需要否定。

有人可以让我geometry shader将法线显示为http://blogs.agi.com/insight3d/index.php/2008/10/23/geometry-shader-for-debugging-normals/所示的线条(那些着色器根本不起作用,它似乎在顶点和片段着色器之间的数据中丢失)。

P.S。我不确定我是否做得很好(启动OpenGL和GLSL),所以任何建议也表示赞赏。

编辑: 我通过示例

制作了简单的geometry shader
// This is a very simple pass-through geometry shader
#version 330 core

layout (triangles) in;
layout (triangle_strip, max_vertices = 145) out;

in VS_GS_VERTEX
{
    vec2 UV;
    vec3 vs_worldpos;
    vec3 vs_normal;
} vertex_in[];

out GS_FS_VERTEX
{
    vec2 UV;
    vec3 vs_worldpos;
    vec3 vs_normal;
} vertex_out;

uniform float uNormalsLength = 0.5;

void main()
{
    int i;
    // Loop over the input vertices
    for (i = 0; i < gl_in.length(); i++)
    {
        vertex_out.UV = vertex_in[i].UV;
        vertex_out.vs_worldpos = vertex_in[i].vs_worldpos;
        vertex_out.vs_normal = vertex_in[i].vs_normal;

        // Copy the input position to the output
        gl_Position = gl_PositionIn[i];
        EmitVertex();

        gl_Position = gl_ModelViewProjectionMatrix * (gl_PositionIn[i] + (vec4(vertex_in[i].vs_normal, 0) * uNormalsLength));
        gl_FrontColor = vec4(0.0, 0.0, 0.0, 1.0); //gl_FrontColorIn[i];
        EmitVertex();      
    }
    // End the primitive. This is not strictly necessary
    // and is only here for illustrative purposes.
    EndPrimitive();
}

但我不知道它需要gl_ModelViewProjectionMatrix(似乎已弃用)并且结果看起来很糟糕,似乎包括法线在内的一切都被剥离了。在glPolygonMode(GL_FRONT,GL_LINE)模式下的图片,纹理也试图映射到那些。 enter image description here

1 个答案:

答案 0 :(得分:5)

看起来,你是在一次通过中完成所有操作而实际上每个输入三角形发出6个顶点。这不是你想要的。

要么两次通过,即一次通过网格,另一次通过法线,或尝试发射原始三角形和退化三角形用于法线。为简单起见,我会选择两遍版本:

在渲染循环中:

  • 渲染地形
  • 当且仅当要渲染调试几何体时
    • 启用调试法线着色器
    • 第二次渲染地形网格,将POINTS传递到顶点着色器

为了完成这项工作,你需要一个第二个程序对象,就像你之前链接到的博客文章一样,由一个简单的通过顶点着色器,下面的几何着色器和一个用于着色的片段着色器组成。代表法线的线条。

顶点和片段着色器应该没问题。假设你有一个平滑的网格,即你有实际的平均顶点法线,你可以简单地传入点并发射线。

#version 330 core

// assuming you have vertex normals, you need to render a vertex
// only a single time. with any other prim type, you may render
// the same normal multiple times
layout (points) in; 

// Geometry shaders can only output points, line strips or triangle
// strips by definition. you output a single line per vertex. therefore, 
// the maximum number of vertices per line_strip is 2. This is effectively
// the same as rendering distinct line segments.
layout (line_strip, max_vertices = 2) out;

in      vec3  vs_normal[];
uniform float normal_scale = 0.5; // don't forget: this is the default value! 

/* if you're never going to change the normal_scale, consider simply putting a 
  constant there instead:
  const float normal_scale = 0.5;
*/

void main()
{
    // we simply transform and emit the incoming vertex - this is v0 of our
    // line segment
    vec4 v0     = gl_in[0].gl_Position;
    gl_Position = gl_ModelViewProjectionMatrix * v0;
    EmitVertex();

    // we calculate v1 of our line segment
    vec4 v1     = v0 + vec4(vs_normal[0] * normal_scale, 0);
    gl_Position = gl_ModelViewProjectionMatrix * v1;
    EmitVertex();

    EndPrimitive();
}

警告:未经测试的代码!

这可能很简单。在片段着色器中添加一个制服,以便您可以根据需要为法线着色,或者只是导出一个恒定的颜色。

注意:此代码仍使用gl_ModevelViewProjectionMatrix。如果你正在编写GL核心代码,请考虑用你自己的东西替换传统的GL结构,比如矩阵堆栈!

注2 :您的几何着色器通常称为通过着色器的内容。首先,您对传入数据进行处理,这不仅仅是将传入值分配给传出值。其次,如果生成几何体,它如何成为一个直通着色器?传递意味着,除了传递传入值到下一个着色器阶段之外,您不会执行任何操作。