第三十九章 几何着色器总结

下面看下几何着色器:是顶点和片段着色器之间的着色器。
输入是一个图元(点或者三角形)的一组顶点,在顶点发送到下一阶段着色器之前可以随意变换,能够将这组顶点变换为完全不同的图元,并且生成比原来更多的顶点。
举例:
#version 330 core
layout (points) in;————需要声明从顶点着色器输入的图元类型。这需要在in关键字前声明一个布局修饰符
layout (line_strip, max_vertices = 2) out;————指定几何着色器输出的图元类型。输出一条线,并将最大顶点数设置为2

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:绘制GL_POINTS图元时(1)。
lines:绘制GL_LINES或GL_LINE_STRIP时(2)
lines_adjacency:GL_LINES_ADJACENCY或GL_LINE_STRIP_ADJACENCY(4)
triangles:GL_TRIANGLES、GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN(3)
triangles_adjacency:GL_TRIANGLES_ADJACENCY或GL_TRIANGLE_STRIP_ADJACENCY(6)
输出布局修饰符也可以接受几个图元值:
points
line_strip
triangle_strip

可以使用输入图元创建几乎任意的形状。如果要生成一个三角形,将输出定义为triangle_strip,并输出3个顶点。

几何着色器同事希望设置一个最大能够输出的顶点数量,如果超过了这个值,OpenGL将不会绘制多出的顶点,这个也可以在out关键字的布局修饰符中设置。

下面需要用接口块来获取前一着色器的输出,GLSL有内建变量如下:
in gl_Vertex
{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[];
上述是声明为一个数组,因为大多数的渲染图元包含多于1个顶点,而几何着色器的输入是一个图元的所有顶点。
可以使用2个几何着色器函数,EmitVertex和EndPrimitive,来生成新的数据:
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被调用时,所有发射出的(Emitted)顶点都会合成为指定的输出渲染图元。
在一个或多个EmitVertex调用之后重复调用EndPrimitive能够生成多个图元。

下面是使用几何着色器:
在标准化设备坐标的z平面上绘制四个点:
float points[] = {
-0.5f, 0.5f, // 左上
0.5f, 0.5f, // 右上
0.5f, -0.5f, // 右下
-0.5f, -0.5f // 左下
};
顶点着色器如下:
#version 330 core
layout (location = 0) in vec2 aPos;

void main()
{
gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
}
片段着色器进行硬编码,所有点输出为绿色:
#version 330 core
out vec4 FragColor;

void main()
{
FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
最后进行绘制:
shader.use();
glBindVertexArray(VAO);
glDrawArrays(GL_POINTS, 0, 4);
则可以看到四个绿色的点。
下面创建一个几何着色器,接收点图元:
#version 330 core
layout (points) in;
layout (points, max_vertices = 1) out;

void main() {
gl_Position = gl_in[0].gl_Position;
EmitVertex();
EndPrimitive();
}
当然,几何着色器也需要编译和链接:
geometryShader = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometryShader, 1, &gShaderCode, NULL);
glCompileShader(geometryShader);

glAttachShader(program, geometryShader);
glLinkProgram(program);

下面,可以利用几何着色器在每个点的位置上绘制一个房子。需要将几何着色器输出设置为triangle_strip。并绘制3个三角形,两个组成正方形,一个组成房顶。
补充:一个三角形带至少需要3个顶点,生成N-2个三角形。
使用三角形带作为几何着色器的输出,用正确的顺序生成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:左下
EmitVertex();
gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:右下
EmitVertex();
gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:左上
EmitVertex();
gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:右上
EmitVertex();
gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:顶部
EmitVertex();
EndPrimitive();
}

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

物体进行”爆破“:
沿着三角形的法向量位移每个顶点,首先需要计算法向量,计算垂直于三角形表面的向量。
方法:使用叉乘获取垂直于其他两个向量的一个向量。
使用3个输入顶点坐标来获取法向量:
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));
}
上面使用减法获取了两个平行于三角形表面的向量a和b,因为两个向量相减能够得到这两个向量之间的差值,并且三个点都位于三角平面上,
对任意两个向量相减都能得到一个平行于平面的向量。
当然,交换cross函数中a和b的位置,会得到一个指向相反方向的法向量(顺序很重要)
下面,创建一个explode函数,使用法向量和顶点位置向量作为参数,该函数会返回一个新的向量,是位置向量沿着法线向量进行位移之后的结果:
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);
}
完整的几何着色器如下:
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

in VS_OUT {
vec2 texCoords;
} gs_in[];

out vec2 TexCoords;

uniform float time;

vec4 explode(vec4 position, vec3 normal) { … }

vec3 GetNormal() { … }

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();

}
记得设置time:
shader.setFloat(“time”, glfwGetTime());

最后看下法向量可视化:
光照错误很常见的原因是法向量错误。可能是不正确加载顶点数据,错误地将其定义为顶点属性或在着色器中不正确使用导致的。
检测法向量是否正确,比较好的方式:法向量可视化。
思路是:首先不使用几何着色器正常绘制场景。然后再次绘制场景,但这次只显示通过几何着色器生成法向量。几何着色器接收一个三角形图元,并沿着
法向量生成三条线——每个顶点一个法向量。伪代码如下:
shader.use();
DrawScene();
normalDisplayShader.use();
DrawScene();

使用模型提供的顶点法线,在将法线变换到观察空间坐标之前,先使用发现矩阵变换一次(几何着色器接收的位置向量是观察空间坐标的)。变换在顶点着色器中:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out VS_OUT {
vec3 normal;
} vs_out;

uniform mat4 view;
uniform mat4 model;

void main()
{
gl_Position = view * model * vec4(aPos, 1.0);
mat3 normalMatrix = mat3(transpose(inverse(view * model)));
vs_out.normal = normalize(vec3(vec4(normalMatrix * aNormal, 0.0)));
}
变换后的观察空间法向量会以接口块的形式传递到下个着色器阶段。几何着色器会接收每一个顶点,包括一个位置向量和一个法向量。并在每个位置绘制一个法线
向量:
#version 330 core
layout (triangles) in;
layout (line_strip, max_vertices = 6) out;

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); // 第一个顶点法线
GenerateLine(1); // 第二个顶点法线
GenerateLine(2); // 第三个顶点法线
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值