光照
在上一节我们实现了材质.
材质用于表征一个表面的各种属性.
但是相同的材质在不同光照的情况下显现出的结果不同.例如:一件白衣服在不同灯光下显现的颜色不同.
所以最终像素点上的颜色应该由光照和材质共同决定.
光照模型有很多种,常见的有Phong光照模型,Blinn-Phong光照模型.
我们在这一节实现一个Blinn-Phong光照模型.
Light类
首先我们定义一个Light类,用于描述光源.
class Light { public: glm::vec3 position; glm::vec3 color; float radiant;
public: Light(){ position = glm::vec3(0.0f, 0.0f, 0.0f); color = glm::vec3(1.0f, 1.0f, 1.0f); radiant = 10.0f; } Light(glm::vec3 pos, glm::vec3 col, float rad) { position = pos; color = col; radiant = rad; } ~Light() {} };
|
现实生活中的光源很多,最常见的就是太阳光,灯光.
其中太阳光是一种平行光,我们定义一个平行光
#pragma once
#include "Light/Light.hpp"
class DirectionalLight : public Light { public: glm::vec3 direction;
public: DirectionalLight() : Light() { direction = glm::vec3(0.0f, -1.0f, 0.0f); }
DirectionalLight(glm::vec3 direction) : Light() { this->direction = glm::normalize(direction); }
DirectionalLight(glm::vec3 direction,glm::vec3 position, glm::vec3 color, float radiant) :Light(position, color, radiant) { this->direction = glm::normalize(direction); }
~DirectionalLight(){} };
|
另外我们还可以定义点光源,但这里先按下不表.
Fragment Shader
我们在Fragment Shader中定义光源
struct DirLight { vec3 dir; vec3 color; vec3 radiant; }; struct PointLight { vec3 pos; vec3 color; vec3 radint;
float constant; float linear; float quadratic; };
#define NR_POINT_LIGHTS 4 uniform DirLight dirLight; uniform PointLight pointLight[NR_POINT_LIGHTS];
|
传递数据
我们定义了一个场景类用于管理所有物体,光源等.
在具体Material类中,我们传递光源数据给Shader
unsigned short directionalLightDirLoc = glGetUniformLocation(shader->shaderProgramID, "dirLight.dir"); unsigned short directionalLightColorLoc = glGetUniformLocation(shader->shaderProgramID, "dirLight.color"); unsigned short directionalLightRadiantLoc = glGetUniformLocation(shader->shaderProgramID, "dirLight.radiant"); glm::vec3 directionalLightDir = Singleton<Scene>::Instance().directionalLights.at(0)->direction; glm::vec3 directionalLightColor = Singleton<Scene>::Instance().directionalLights.at(0)->color; float directionalLightRadiant = Singleton<Scene>::Instance().directionalLights.at(0)->radiant; glUniform3fv(directionalLightDirLoc, 1, glm::value_ptr(directionalLightDir)); glUniform3fv(directionalLightColorLoc, 1, glm::value_ptr(directionalLightColor)); glUniform1f(directionalLightRadiantLoc, directionalLightRadiant);
|
Blinn-Phong光照模型
Blinn-Phong光照模型是Phong光照模型的改进版本.
Phong光照模型认为光源是平行于视线的,而Blinn-Phong光照模型认为光源是平行于表面的.
Blinn-Phong光照模型的计算公式如下:
vec3 N = normalize(Normal); vec3 L = normalize(lightDir); vec3 V = normalize(viewDir); vec3 R = reflect(-L, N);
float diffuse = max(dot(N, L), 0.0); float specular = pow(max(dot(R, V), 0.0), 32.0);
vec3 ambient = vec3(0.1); vec3 diffuseColor = vec3(1.0, 1.0, 1.0); vec3 specularColor = vec3(1.0, 1.0, 1.0);
vec3 result = (ambient + diffuse * diffuseColor + specular * specularColor) * objectColor;
|
最终Shader
我们可以得出最终的Shader如下:
#version 330 core struct DirLight { vec3 dir; vec3 color; vec3 radiant; }; struct PointLight { vec3 pos; vec3 color; vec3 radint;
float constant; float linear; float quadratic; };
#define NR_POINT_LIGHTS 4
in vec3 modelNormal; in vec3 worldPos; in vec3 worldNormal; in vec2 uv;
uniform DirLight dirLight; uniform PointLight pointLight[NR_POINT_LIGHTS];
uniform sampler2D albedo; uniform sampler2D metallic; uniform sampler2D roughness; uniform sampler2D emission; uniform sampler2D ao;
uniform vec3 viewPos;
out vec4 FragColor;
vec3 computeDirLight(DirLight light, vec3 norm, vec3 viewDir) { vec3 lightDir = normalize(-light.dir); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * light.color; vec3 halfDir = normalize(lightDir + viewDir); float spec = pow(max(dot(norm, halfDir), 0.0), 32.0); vec3 specular = spec * light.color; return diffuse + specular; }
vec3 computePointLight(PointLight light, vec3 norm, vec3 fragPos, vec3 viewDir) { vec3 lightDir = normalize(light.pos - fragPos); float distance = length(light.pos - fragPos); float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * light.color * attenuation; vec3 halfDir = normalize(lightDir + viewDir); float spec = pow(max(dot(norm, halfDir), 0.0), 32.0); vec3 specular = spec * light.color * attenuation; return diffuse + specular; }
void main() { vec3 objectColor = texture(albedo, uv).rgb; vec3 norm = normalize(modelNormal); vec3 viewDir = normalize(viewPos - worldPos); vec3 result = 0.1 * objectColor;
result += computeDirLight(dirLight, norm, viewDir);
FragColor = vec4(result * objectColor, 1.0); }
|
结果
运行,可以看到我们的角色在光照下的表现了.
