OpenGL入门回顾

0.渲染管道

image.png

渲染管道通过EBO向顶点着色器(Vertex Shader)输入模型顶点坐标、法线向量、贴图坐标等信息。


image.png
//EBO示例代码
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
GL_STATIC_DRAW);

cpu计算的光源坐标、模型矩阵、相机矩阵、透视矩阵等数据通过uniform传入渲染管道中。

//uniform示例代码
int vertexColorLocation = glGetUniformLocation(shaderProgram,
"ourColor");
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

然后片元着色器(Fragment Shader)通过从顶点着色器和Uniform传入的贴图坐标、顶点坐标和渲染颜色等信息,逐个计算每一个片元的渲染颜色值,WebGL、Three.js、Cesium等的自定义Shader主要就是自定义片元着色器的GLSL代码。

//顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos; // position has attribute position 0
out vec4 vertexColor; // specify a color output to the fragment shader
void main()
{
gl_Position = vec4(aPos, 1.0); // we give a vec3 to vec4’s constructor
vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // output variable to dark-red
}
//片元着色器
#version 330 core
out vec4 FragColor;
in vec4 vertexColor; // input variable from vs (same name and type)
void main()
{
FragColor = vertexColor;
}

通常一个渲染管道(ShaderProgram,包含一套顶点着色器和片元着色器代码)负责渲染一个独立的物体,在渲染多个物体时,需依次绑定这些物体使用的渲染管道(也可能使用相同的渲染管道),并向管道传入属于该物体的模型顶点坐标、变换矩阵、顶点法线向量等信息。

1.模型及相机位姿变换

渲染管线中一共有5种坐标系,主要的3种是本地坐标系:记录三维模型顶点坐标;世界坐标系:记录三维场景中的物体的位姿;相机坐标系:从相机观察视角记录物体位姿。经过一系列转换可将模型渲染到相机成像平面。
Vclip = Mprojection ·Mview · Mmodel ·Vlocal


image.png

Vlocal是模型文件中存储的顶点坐标,Mmodel是根据模型在世界坐标系中的位姿计算的变换矩阵,Mview是根据相机在世界坐标系中的位姿计算的相机变换矩阵,Mprojection是根据相机内参计算的透视投影矩阵,将模型投影到平面进行渲染。

//cpu计算用于渲染的位姿矩阵
glm::mat4 model = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(-55.0f),
glm::vec3(1.0f, 0.0f, 0.0f));

glm::mat4 view = glm::mat4(1.0f);
// note that we’re translating the scene in the reverse direction
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));

glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f,
100.0f);
//顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;

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

void main()
{
// note that we read the multiplication from right to left
gl_Position = projection * view * model * vec4(aPos, 1.0);
}

2.光照

光照主要包含3种类型,环境光:即无直射光时的暗光颜色;漫反射光:直射光对粗糙物体的光照颜色;镜面反射光:直射光对镜面物体的光照颜色。三种光组合起来模拟真实环境的光照效果。

image.png

环境光

//片元着色器片段
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
vec3 result = ambient * objectColor;
FragColor = vec4(result, 1.0);

环境光渲染效果,白色方块示意光源颜色

image.png

漫反射光
漫反射光首先计算光源到片元位置的光照方向,然后结合从VBO传入的顶点法线向量,计算出光线入射片元平面的角度,片元的法线向量和入射光线方向的角度越大,片元的光照亮度越弱。
image.png

//顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

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

out vec3 FragPos;
out vec3 Normal;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = aNormal;
}

片元着色器中光源坐标和光源颜色通过uniform传入

//片元着色器,漫反射+环境光
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 lightColor;

void main()
{
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;

vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

vec3 result = (ambient + diffuse) * objectColor;
FragColor = vec4(result, 1.0);
}

漫反射+环境光渲染效果

漫反射+环境光渲染效果.png

镜面反射光
相比漫反射光照强度计算,镜面反射的光照强度计算需要多引入相机坐标,光源在片元上反射的射出方向和相机到片元的观察方向之间的角度越小,光照强度越大。
image.png

//片元着色器,漫反射+环境光+反射光
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 viewPos;

void main()
{
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;

vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}

上述代码中spec计算过程的数字32指的是当前片元的反射强度(光泽),光泽数字越大,镜面效果越强,即物体上的高光范围越小。

光泽强度变化.png

光源镜面反射渲染.png

从顶点着色器通过out传入片元着色器in的顶点信息,例如上文中顶点坐标和顶点法线向量FragPos和Normal,会进行插值计算出片元处平滑的数据。上文光照渲染将顶点坐标、顶点法线向量、相机坐标和视点坐标传入到片元着色器中,在每一个片元中使用从顶点数据插值计算出的数据进行渲染能够得到平滑真实的效果,若将光照强度在顶点着色器中进行计算,片元着色器使用从顶点着色器插值估算的光照强度进行渲染,能够降低渲染开销,但真实性欠佳。

3.材质

材质描述的是模型片元对光照的反应,包含4个参数: 环境光颜色、漫反射颜色、镜面反射颜色和镜面反射光泽强度。
如下片元着色器代码将整个模型通过uniform设置为同一种材质

//片元着色器片段,整个物体通过uniform设置为同一种材质
#version 330 core
...
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
//设置三种情况光源颜色
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
uniform Material material;
void main()
{
...
// ambient
vec3 ambient = light.ambient * material.ambient;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse =  light.diffuse* (diff * material.diffuse);
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0),
material.shininess);
vec3 specular = light.specular* (spec * material.specular);
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}

材质贴图
材质贴图包含漫反射贴图和镜面反射贴图,漫反射贴图提供环境光和漫反射光照射的颜色,镜面反射贴图提供镜面反射的颜色,通过贴图的方式代替上文的单一材质。

漫反射贴图.png

镜面反射贴图.png

//顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

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

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

void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = aNormal;
TexCoords = aTexCoords;
}
//片元着色器
#version 330 core

in vec2 TexCoords;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 viewPos;

struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
//设置三种情况光源颜色
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
uniform Material material;
void main()
{
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position- FragPos);
float diff = max(dot(norm, lightDir), 0.0);

vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);

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));
FragColor = vec4(ambient + diffuse + specular, 1.0);
}
image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容