新版OpenGL学习入门(三)——对着色器的一些理解

本文深入探讨OpenGL中的顶点、几何和片段着色器,详细解释它们如何在图形渲染管线中工作,特别是在创建爆炸效果和处理光照计算方面的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在写完OpenGL项目后,再次回顾并总结一下用到的一些知识。
注意此博文可能需要先入门,否则可能有些难懂
我做了一个简易的皮卡丘版的跳一跳,有兴趣的可以看一下我的Github:Jumping Pikachu
Pikachu
整个项目的一个亮点是在跳跃失败了以后会产生爆破的效果,这个效果需要用到几何着色器

关于着色器

我整个项目的学习链接:LearnOpenGL

在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,3D坐标转为2D坐标的处理过程叫做图形渲染管线

而在图形渲染管线也分为好几个步骤,以下为图示
图形渲染管线的过程
其中三个着色器就是把数据处理成另一类数据然后交给下一个着色器继续处理,最后出来就是我们要的内容了。

顶点着色器(vs) 是最初用来处理顶点坐标的,一般会对坐标做一些计算
几何着色器(gs) 是对原坐标进行一些修改,个人感觉适用于一些特效
片段着色器(fs) 是对图形进行涂色,并最终输出。

注意:这三个顺序是固定的,其中几何着色器并不是一定要用的。

顶点着色器

一般一个点储存了它的三维坐标、三维法向量和二维的纹理坐标。
其中法向量是为了计算光线的,而纹理坐标则是为了纹理贴图。
以下的代码是我项目中的,其中out是传到后面一个着色器的东西,而gl_Position是该点的输出。

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

几何着色器

集合着色器可以对坐标进行一些修改,而我的项目中皮卡丘爆破的场景就是通过这个进行修改的。
爆破的效果是通过对每一个碎片向它的法向量移动一段距离而获得的,一下是教程中的爆破效果。
爆破
以下是我项目的代码

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

片段着色器

片段着色器就是最后进行上色的部分,由于颜色的亮度和光照有关,因此其中包含计算光照和颜色。
对于不同反射等计算是比较困难的,我就借鉴了教程上的部分。

#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);
    // diffuse shading
    float diff = max(dot(normal, lightDir), 0.0);
    // specular shading
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // combine results
    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);
}

总体来说,着色器虽然较难入手,但是着色器可以很有效的帮助我们进行变换等其他操作,尤其是十分优秀的几何着色器。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值