在写完OpenGL项目后,再次回顾并总结一下用到的一些知识。
注意此博文可能需要先入门,否则可能有些难懂
我做了一个简易的皮卡丘版的跳一跳,有兴趣的可以看一下我的Github:Jumping Pikachu
整个项目的一个亮点是在跳跃失败了以后会产生爆破的效果,这个效果需要用到几何着色器。
关于着色器
我整个项目的学习链接:LearnOpenGL
在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,3D坐标转为2D坐标的处理过程叫做图形渲染管线
而在图形渲染管线也分为好几个步骤,以下为图示
其中三个着色器就是把数据处理成另一类数据然后交给下一个着色器继续处理,最后出来就是我们要的内容了。
- 顶点着色器(vs) 是最初用来处理顶点坐标的,一般会对坐标做一些计算
- 几何着色器(gs) 是对原坐标进行一些修改,个人感觉适用于一些特效
- 片段着色器(fs) 是对图形进行涂色,并最终输出。
注意:这三个顺序是固定的,其中几何着色器并不是一定要用的。
顶点着色器
一般一个点储存了它的三维坐标、三维法向量和二维的纹理坐标。
其中法向量是为了计算光线的,而纹理坐标则是为了纹理贴图。
以下的代码是我项目中的,其中out是传到后面一个着色器的东西,而gl_Position是该点的输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords;
out VS_OUT { vec3 FragPos; vec3 Normal; vec2 texCoords; } vs_out;
uniform mat4 model; uniform mat4 view; uniform mat4 projection;
void main() { vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); vs_out.Normal = mat3(transpose(inverse(model))) * aNormal; vs_out.texCoords = aTexCoords;
gl_Position = projection * view * model * vec4(aPos, 1.0); }
|
几何着色器
集合着色器可以对坐标进行一些修改,而我的项目中皮卡丘爆破的场景就是通过这个进行修改的。
爆破的效果是通过对每一个碎片向它的法向量移动一段距离而获得的,一下是教程中的爆破效果。
以下是我项目的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #version 330 core layout (triangles) in; layout (triangle_strip, max_vertices = 3) out;
in VS_OUT { vec3 FragPos; vec3 Normal; vec2 texCoords; } gs_in[];
out vec3 FragPos; out vec3 Normal; out vec2 TexCoords;
uniform float time;
vec4 explode(vec4 position, vec3 normal) { float magnitude = 2.0; vec3 direction; vec4 temp; if (time > 0.0) { direction = normal * (-log(1.0- time)) * magnitude; temp = position + vec4(direction, 0.0); } else { temp = position; } return temp; }
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); Normal = gs_in[0].Normal; FragPos = gs_in[0].FragPos; TexCoords = gs_in[0].texCoords; EmitVertex(); gl_Position = explode(gl_in[1].gl_Position, normal); Normal = gs_in[1].Normal; FragPos = gs_in[1].FragPos; TexCoords = gs_in[1].texCoords; EmitVertex(); gl_Position = explode(gl_in[2].gl_Position, normal); Normal = gs_in[2].Normal; FragPos = gs_in[2].FragPos; TexCoords = gs_in[2].texCoords; EmitVertex(); EndPrimitive(); }
|
片段着色器
片段着色器就是最后进行上色的部分,由于颜色的亮度和光照有关,因此其中包含计算光照和颜色。
对于不同反射等计算是比较困难的,我就借鉴了教程上的部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #version 330 core out vec4 FragColor;
struct Material { sampler2D diffuse; sampler2D specular; float shininess; };
struct DirLight { vec3 direction;
vec3 ambient; vec3 diffuse; vec3 specular; };
in vec3 FragPos; in vec3 Normal; in vec2 TexCoords;
uniform vec3 viewPos; uniform DirLight dirLight; uniform Material material;
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
void main() { vec3 norm = normalize(Normal); vec3 viewDir = normalize(viewPos - FragPos); vec3 result = CalcDirLight(dirLight, norm, viewDir);
FragColor = vec4(result, 1.0); }
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir) { vec3 lightDir = normalize(-light.direction); float diff = max(dot(normal, lightDir), 0.0); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); return (ambient + diffuse + specular); }
|
总体来说,着色器虽然较难入手,但是着色器可以很有效的帮助我们进行变换等其他操作,尤其是十分优秀的几何着色器。
搬运自CSDN:https://blog.csdn.net/yueyue200830/article/details/88071644