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計算的光源坐標、模型矩陣、相機矩陣、透視矩陣等數(shù)據(jù)通過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,包含一套頂點著色器和片元著色器代碼)負責(zé)渲染一個獨立的物體,在渲染多個物體時,需依次綁定這些物體使用的渲染管道(也可能使用相同的渲染管道),并向管道傳入屬于該物體的模型頂點坐標、變換矩陣、頂點法線向量等信息。

1.模型及相機位姿變換

渲染管線中一共有5種坐標系,主要的3種是本地坐標系:記錄三維模型頂點坐標;世界坐標系:記錄三維場景中的物體的位姿;相機坐標系:從相機觀察視角記錄物體位姿。經(jīng)過一系列轉(zhuǎn)換可將模型渲染到相機成像平面。
Vclip = Mprojection ·Mview · Mmodel ·Vlocal


image.png

Vlocal是模型文件中存儲的頂點坐標,Mmodel是根據(jù)模型在世界坐標系中的位姿計算的變換矩陣,Mview是根據(jù)相機在世界坐標系中的位姿計算的相機變換矩陣,Mprojection是根據(jù)相機內(nèi)參計算的透視投影矩陣,將模型投影到平面進行渲染。

//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種類型,環(huán)境光:即無直射光時的暗光顏色;漫反射光:直射光對粗糙物體的光照顏色;鏡面反射光:直射光對鏡面物體的光照顏色。三種光組合起來模擬真實環(huán)境的光照效果。

image.png

環(huán)境光

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

環(huán)境光渲染效果,白色方塊示意光源顏色

image.png

漫反射光
漫反射光首先計算光源到片元位置的光照方向,然后結(jié)合從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傳入

//片元著色器,漫反射+環(huán)境光
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);
}

漫反射+環(huán)境光渲染效果

漫反射+環(huán)境光渲染效果.png

鏡面反射光
相比漫反射光照強度計算,鏡面反射的光照強度計算需要多引入相機坐標,光源在片元上反射的射出方向和相機到片元的觀察方向之間的角度越小,光照強度越大。
image.png

//片元著色器,漫反射+環(huán)境光+反射光
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計算過程的數(shù)字32指的是當(dāng)前片元的反射強度(光澤),光澤數(shù)字越大,鏡面效果越強,即物體上的高光范圍越小。

光澤強度變化.png

光源鏡面反射渲染.png

從頂點著色器通過out傳入片元著色器in的頂點信息,例如上文中頂點坐標和頂點法線向量FragPos和Normal,會進行插值計算出片元處平滑的數(shù)據(jù)。上文光照渲染將頂點坐標、頂點法線向量、相機坐標和視點坐標傳入到片元著色器中,在每一個片元中使用從頂點數(shù)據(jù)插值計算出的數(shù)據(jù)進行渲染能夠得到平滑真實的效果,若將光照強度在頂點著色器中進行計算,片元著色器使用從頂點著色器插值估算的光照強度進行渲染,能夠降低渲染開銷,但真實性欠佳。

3.材質(zhì)

材質(zhì)描述的是模型片元對光照的反應(yīng),包含4個參數(shù): 環(huán)境光顏色、漫反射顏色、鏡面反射顏色和鏡面反射光澤強度。
如下片元著色器代碼將整個模型通過uniform設(shè)置為同一種材質(zhì)

//片元著色器片段,整個物體通過uniform設(shè)置為同一種材質(zhì)
#version 330 core
...
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
//設(shè)置三種情況光源顏色
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);
}

材質(zhì)貼圖
材質(zhì)貼圖包含漫反射貼圖和鏡面反射貼圖,漫反射貼圖提供環(huán)境光和漫反射光照射的顏色,鏡面反射貼圖提供鏡面反射的顏色,通過貼圖的方式代替上文的單一材質(zhì)。

漫反射貼圖.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;
};
//設(shè)置三種情況光源顏色
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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容