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内,不确定),自动将顶点着色器输出的像素坐标绘制成图元。如本次举例的三个点绘制成三角形。
几何着色器将第三步输出的图元进一步扩展成更细的多边形(类似有限元分割),且这一步是可自定义的,可以借助一些曲面细分的技术,使靠近计算机的部分具有更丰富的细节,远离计算机的部分具有较少的细节。
我并没有尝试过自定义几何着色器,所以这一步具体细节不太清楚。
光栅化将几何着色器输出的图元转换成一块块离散的片元(对应像素坐标中的一个个离散的像素)。
这是将连续模拟信号转换成离散信号的过程,因为在此之前,顶点的坐标仍是连续的,具有小数的,而最终屏幕上的像素坐标是离散的,整数形式的。
光栅化步骤是非自定义的,由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);