[OpenGL] OpenGL图像渲染管线流程

在这里插入图片描述

第一步:顶点数据传入GPU

https://blog.csdn.net/weixin_44179561/article/details/124275761

通过缓冲对象(BO)将在CPU内的顶点数据传入GPU,通过顶点数组对象(VAO)告诉OpenGL该如何解释这些数据

索引缓冲对象(EBO)告诉OpenGL,这些顶点数据是如何绘制构成图形的

第二步:顶点着色器处理接收到的顶点数据

第一步中传入的顶点坐标是位于物体坐标系上的坐标:

float vertices[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
};

如上述坐标就是一个位于 Z=0 平面的三维三角形

顶点着色器所做的工作:将第一步中传入的三维物体坐标投影到像素屏幕坐标

其中包括坐标系的转换(物体坐标系→世界坐标系→摄像机坐标系→屏幕坐标系),裁剪,映射

	//物体坐标系——>世界坐标系——>摄像机坐标系——>屏幕坐标系
	//三维点坐标——————————————————>屏幕像素点坐标
    gl_Position = projection * view * model * vec4(aPos, 1.0);

顶点着色器最终将第一步中传入的三维物体坐标与屏幕的像素坐标一一对应,并通过预定义的gl_position变量向后续处理步骤传递

我所定义过的一个顶点着色器:

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

out vec2 TexCoords;
out vec3 FragPos;
out vec3 Normal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    TexCoords = aTexCoords;
	//物体坐标系——>世界坐标系——>摄像机坐标系——>屏幕坐标系
	//三维点坐标——————————————————>屏幕像素点坐标
    gl_Position = projection * view * model * vec4(aPos, 1.0);
	FragPos = vec3(model * vec4(aPos,1));
	Normal = mat3(transpose(inverse(model))) * aNormal;
}

第三步:图元装配

这一步是非自定义的,即是由OpenGL自动完成的,OpenGL根据用户预先提到的绘制顺序(应该是在
EBO内,不确定),自动将顶点着色器输出的像素坐标绘制成图元。如本次举例的三个点绘制成三角形。

第四步:几何着色器

几何着色器将第三步输出的图元进一步扩展成更细的多边形(类似有限元分割),且这一步是可自定义的,可以借助一些曲面细分的技术,使靠近计算机的部分具有更丰富的细节,远离计算机的部分具有较少的细节。

在这里插入图片描述
我并没有尝试过自定义几何着色器,所以这一步具体细节不太清楚。

第五步:光栅化

光栅化将几何着色器输出的图元转换成一块块离散的片元(对应像素坐标中的一个个离散的像素)。

这是将连续模拟信号转换成离散信号的过程,因为在此之前,顶点的坐标仍是连续的,具有小数的,而最终屏幕上的像素坐标是离散的,整数形式的。

在这里插入图片描述
光栅化包含两部分工作:

  1. 决定屏幕像素坐标中哪些像素被图元覆盖,在渲染过程中要进行涂色
  2. 如果顶点坐标包括颜色信息(如RGB),通过插值的方式将颜色信息逐一分配到个个像素上

光栅化步骤是非自定义的,由OpenGL自动完成,最终输出一块块片元。

第六步:片段着色器

片段着色器计算一个片元的最终颜色,决定最终的渲染效果

根据法向量和片元位置,以及用户自定义的物体材质、光源位置、光照模型等,计算片元的最终颜色,并输出:

in vec3 Normal;
in vec3 FragPos;
out vec4 FragColor;

选用冯氏光照模型,即一个片元的最终颜色由三个部分组成:环境光、漫反射、镜面反射

在这里插入图片描述

https://blog.csdn.net/klscer/article/details/123591585

光源形式包括点光源、定向光(平行光)、聚光,各自有计算形式。

https://learnopengl-cn.github.io/02%20Lighting/05%20Light%20casters/

叠加多个光源计算求得的环境光、漫反射、镜面反射后,输出片元的最终颜色:

	vec3 result;
	//定向光照
	result = CalcDirLight(dirlight,norm,viewDir);
	//点光源
	result = CalcPointLight(pointLight,norm,FragPos,viewDir);

	FragColor = vec4(result,1.0);

我所定义过的一个片段着色器:

#version 330 core

//定向光
struct DirLight{
	vec3 direction;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
};
//点光源
struct PointLight{
	vec3 position;

	//衰减
	float constant;
	float linear;
	float quadratic;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
};
//材质
struct Material{
	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
	float shininess;
};

out vec4 FragColor;

in vec3 Normal;
in vec3 FragPos;

uniform vec3 viewPos;
uniform Material material;
uniform PointLight pointLight;
uniform DirLight dirlight;


vec3 CalcDirLight(DirLight light,vec3 normal,vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);

void main()
{
	//属性
	//法向量
	vec3 norm = normalize(Normal);
	//视线
	vec3 viewDir = normalize(viewPos - FragPos);

	vec3 result;
	//定向光照
	result = CalcDirLight(dirlight,norm,viewDir);
	//点光源
	result = CalcPointLight(pointLight,norm,FragPos,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 * material.ambient;
	vec3 diffuse = light.diffuse * diff * material.diffuse;
	vec3 specular = light.specular * spec * material.specular;

	return (ambient + diffuse + specular);
}
//点光源
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
	vec3 lightDir = normalize(light.position - fragPos);

	//漫反射
	float diff = max(dot(normal,lightDir),0.0);

	//镜面光
	vec3 reflectDir = reflect(-lightDir,normal);
	float spec = pow(max(dot(viewDir,reflectDir),0.0),material.shininess);

	//衰减
	float distance = length(light.position - fragPos);
	float attenuation = 1.0/(light.constant + light.linear * distance + light.quadratic * (distance * distance));

	//合并结果
	vec3 ambient = light.ambient * material.ambient;
	vec3 diffuse = light.diffuse * diff * material.diffuse;
	vec3 specular = light.specular * spec * material.specular;

	ambient *= attenuation;
	diffuse *= attenuation;
	specular *= attenuation;

	return (ambient + diffuse + specular);
}

第七步:测试与混合

这一步非自定义,由OpenGL自动完成,只要用户启用了深度测试,OpenGL便会根据深度(片元距离摄像机的距离),计算哪些

片元是能被摄像机看见的,哪些片元是被其他片元遮挡的,并渲染能被摄像机看到的片元。

简单来说就是完成了一个遮挡与被遮挡的效果。

	//启动深度测试
	glEnable(GL_DEPTH_TEST);
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值