Learn OpenGL 笔记5.9 Geometry Shader(几何渲染器)

在顶点和片段着色器之间有一个可选的着色器阶段,称为几何着色器。 几何着色器将形成primitive (set of vertices) 单个图元的一组顶点作为输入,例如 一个点或一个三角形。 然后几何着色器可以在将这些顶点发送到下一个着色器阶段之前,按照它认为合适的方式转换这些顶点。 几何着色器的有趣之处在于它能够将原始图元(顶点集)转换为完全不同的图元,可能生成比最初给出的更多的顶点。

基础知识:

1.输入说明:

在几何着色器开始时,我们需要声明我们从顶点着色器接收到的primitive (set of vertices) 图元输入的类型。 我们通过在 in 关键字前声明一个layout specifier 布局说明符来做到这一点。 此输入布局限定符可以采用以下任何原始值:

#version 330 core
layout (points) in;
layout (line_strip, max_vertices = 2) out;

void main() {    
    gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0); 
    EmitVertex();

    gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0);
    EmitVertex();
    
    EndPrimitive();
}  
  • points: when drawing GL_POINTS primitives (1).
  • lines: when drawing GL_LINES or GL_LINE_STRIP (2).
  • lines_adjacency: GL_LINES_ADJACENCY or GL_LINE_STRIP_ADJACENCY (4).
  • triangles: GL_TRIANGLES, GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN (3).
  • triangles_adjacency : GL_TRIANGLES_ADJACENCY or GL_TRIANGLE_STRIP_ADJACENCY (6).

如果我们选择将顶点绘制为 GL_TRIANGLES,我们应该将输入限定符设置为三角形。 括号内的数字表示单个图元包含的最小顶点数。

2.输出说明:

我们还需要指定几何着色器将输出的基本类型,我们通过 out 关键字前面的布局说明符来完成此操作。 与输入布局限定符一样,输出布局限定符可以采用几个原始值:

points
line_strip
triangle_strip

line_strip 输出两个顶点

triangle_strip 输出三个顶点

2.1 line strip:(线条)

线条将一组点绑定在一起,在它们之间形成一条连续的线,最少有 2 个点。 每个额外的点都会在新点和前一个点之间产生一条新线

2.2 批量检索输出:

//定义了一个interface block的输入数组
in gl_Vertex
{
    vec4  gl_Position;
    float gl_PointSize;
    float gl_ClipDistance[];
} gl_in[];  

2.3 生成新的顶点数据:

我们可以使用称为 EmitVertexEndPrimitive 的 2 个几何着色器函数生成新数据


#version 330 core
layout (points) in;
layout (line_strip, max_vertices = 2) out;
  
void main() {  
    //发出一个顶点  
    gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0); 
    EmitVertex();
    
    //发出一个顶点
    gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0);
    EmitVertex();
    
    //将两个顶点连成一条线
    EndPrimitive();
}    

代码中,每次我们调用 EmitVertex 时,当前设置为 gl_Position 的向量都会添加到输出基元中。 每当调用 EndPrimitive 时,此图元的所有发射顶点都将组合到指定的输出渲染图元中。 通过在一次或多次 EmitVertex 调用后,反复调用 EndPrimitive,可以生成多个图元。

代码中,发出两个顶点,这些顶点从原始顶点位置平移了一个小的偏移量,然后调用 EndPrimitive,将两个顶点组合成一个由 2 个顶点组成的线带。

glDrawArrays(GL_POINTS, 0, 4);  

2.4.Using geometry shaders (使用几何着色器)

先创建四个绿点

shader.use();
glBindVertexArray(VAO);
glDrawArrays(GL_POINTS, 0, 4); 

A triangle strip needs at least 3 vertices and will generate N-2 triangles(一个三角形需要3个顶点,N个顶点渲染出N-2个三角形)

代码:(将会由点123,234,345组合渲染出3个三角形)


#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 5) out;

void build_house(vec4 position)
{    
    gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0);    // 1:bottom-left
    EmitVertex();   
    gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0);    // 2:bottom-right
    EmitVertex();
    gl_Position = position + vec4(-0.2,  0.2, 0.0, 0.0);    // 3:top-left
    EmitVertex();
    gl_Position = position + vec4( 0.2,  0.2, 0.0, 0.0);    // 4:top-right
    EmitVertex();
    gl_Position = position + vec4( 0.0,  0.4, 0.0, 0.0);    // 5:top
    EmitVertex();
    EndPrimitive();
}

void main() {    
    build_house(gl_in[0].gl_Position);
}  

配置颜色的方法:

float points[] = {
    //            这三列数据代表颜色信息
    -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, // top-left
     0.5f,  0.5f, 0.0f, 1.0f, 0.0f, // top-right
     0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // bottom-right
    -0.5f, -0.5f, 1.0f, 1.0f, 0.0f  // bottom-left
};  

//VS代码:

#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec3 aColor;

out VS_OUT {
    vec3 color;
} vs_out;

void main()
{
    gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); 
    vs_out.color = aColor;
}  
//geometry shader中的代码
in VS_OUT {
    vec3 color;
} gs_in[];  

当发射一个顶点时,该顶点将在 fColor 中存储最后存储的值作为该顶点的输出值。

fColor = gs_in[0].color; // gs_in[0] since there's only one input vertex
gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0);    // 1:bottom-left   
EmitVertex();   
gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0);    // 2:bottom-right
EmitVertex();
gl_Position = position + vec4(-0.2,  0.2, 0.0, 0.0);    // 3:top-left
EmitVertex();
gl_Position = position + vec4( 0.2,  0.2, 0.0, 0.0);    // 4:top-right
EmitVertex();
gl_Position = position + vec4( 0.0,  0.4, 0.0, 0.0);    // 5:top
EmitVertex();
EndPrimitive(); 

3.Exploding objects(物体炸了的效果)

#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

//因为传进来的是模型,所以只能是三角形的三个顶点,顶点内部数据是贴图UV坐标
in VS_OUT {
    vec2 texCoords;
} gs_in[];

out vec2 TexCoords; 

uniform float time;

//三个顶点都向法线方向移动
vec4 explode(vec4 position, vec3 normal)
{
    float magnitude = 2.0;
    vec3 direction = normal * ((sin(time) + 1.0) / 2.0) * magnitude; 
    return position + vec4(direction, 0.0);
}

//根据三角形的三个顶点,用叉乘算出法线向量
vec3 GetNormal()
{
    vec3 a = vec3(gl_in[0].gl_Position) - vec3(gl_in[1].gl_Position);
    vec3 b = vec3(gl_in[2].gl_Position) - vec3(gl_in[1].gl_Position);
    return normalize(cross(a, b));
}

void main() {    
    vec3 normal = GetNormal();
    //绘制三个点组成的一个三角形,朝法线方向平移版本的三角形
    gl_Position = explode(gl_in[0].gl_Position, normal);
    TexCoords = gs_in[0].texCoords;
    EmitVertex();
    gl_Position = explode(gl_in[1].gl_Position, normal);
    TexCoords = gs_in[1].texCoords;
    EmitVertex();
    gl_Position = explode(gl_in[2].gl_Position, normal);
    TexCoords = gs_in[2].texCoords;
    EmitVertex();
    EndPrimitive();
}

4.Visualizing normal vectors(显示法线向量)

先普通渲染模型,过程略.......

再进行法线线条的渲染:


#version 330 core
layout (triangles) in;
layout (line_strip, max_vertices = 6) out;

//模型的三角形变换成的3个顶点数据,其中内容是uv法线贴图坐标
in VS_OUT {
    vec3 normal;
} gs_in[];

const float MAGNITUDE = 0.4;
  
uniform mat4 projection;

//对每个顶点都绘制一个可视化的法线线段
void GenerateLine(int index)
{
    gl_Position = projection * gl_in[index].gl_Position;
    EmitVertex();
    gl_Position = projection * (gl_in[index].gl_Position + 
                                vec4(gs_in[index].normal, 0.0) * MAGNITUDE);
    EmitVertex();
    EndPrimitive();
}

void main()
{
    GenerateLine(0); // first vertex normal
    GenerateLine(1); // second vertex normal
    GenerateLine(2); // third vertex normal
}  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值