【OpenGL 学习笔记】三 · 光源 & 多光源

LearnOpenGL CN
《Unity Shader入门精要》自学笔记(三)第六章 基础光照
《Unity Shader入门精要》自学笔记(七)第九章 更复杂的光照 ——光照衰减

平行光

struct DirLight {
    
    
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform DirLight dirLight;

点光源光照衰减的计算

struct PointLight {
    
    
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

	float constant;
	float linear;
	float quadratic;
};
#define MAX_N_POINT_LIGHTS 4
uniform PointLight pointLights[MAX_N_POINT_LIGHTS];

之前没有深究过光照衰减的计算,这次终于知道了

F a t t = 1.0 K c + K l ∗ d + K q ∗ d 2 F_{att} = \frac {1.0 }{K_{c} + K_{l} * d + K_{q} * d^2} Fatt=Kc+Kld+Kqd21.0

d d d 为距离, K c , K l , K q K_{c} ,K_{l} , K_{q} KcKlKq 分别为常数项、一次项(Linear)、二次项(Quadratic)

  • 常数项:通常保持为1.0,它的主要作用是保证分母永远不会比1小
  • 一次项:以线性的方式减少强度。
  • 二次项:让光源以二次递减的方式减少强度

二次项在距离比较小的时候影响会比一次项小很多,但当距离值比较大的时候它就会比一次项更大了。

vec3 Lighting_PointLight(PointLight light, vec3 normal, vec3 viewDir, vec3 baseColor, float specularMask){
    
    
	
	vec3 lightDir = normalize(light.position - vertexPosition);
	float distance = length(light.position - vertexPosition);
	float attenuation = 1 / (light.constant + light.linear * distance + light.quadratic * distance * distance);

	vec3 ambient = baseColor * light.ambient;
	vec3 diffuse = baseColor * light.diffuse * attenuation * max(dot(lightDir, normal), 0.0);
	vec3 specular = specularMask * light.specular * attenuation * max(dot(viewDir, reflect(-lightDir, normal)), 0.0);

	return vec3(ambient + diffuse + specular);
}

聚光灯范围衰减的计算

struct SpotLight{
    
    
	vec3 position;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;

	vec3 direction;
	float cutoff;
	float outerCutoff;
};
uniform SpotLight spotLight;

之前在 Unity 里看到的是一张 Mask,现在是直接计算
在这里插入图片描述
使用聚光灯时,整个屏幕被分为三个部分

  1. 处于内圆锥内:接受全部光照
  2. 处于外圆锥外:只接受环境光照
  3. 处于内外圆锥之间:漫反射和高光根据距离衰减

需要的参数:

  1. 内圆锥角度的余弦值 cutoff
  2. 外圆锥角度的余弦值 outerCutoff
  3. 聚光灯的正方向 direction

进行光照前,计算出光源方向与聚光灯正方向的余弦值,与 cutoff、outerCutoff 的大小关系就可以知道当前像素处于哪个区域
中间区域的衰减算法思路和 InverseLerp() 一致,Lerp() yyds

vec3 Lighting_SpotLight(SpotLight light, vec3 normal, vec3 viewDir, vec3 baseColor, float specularMask){
    
    
	
	vec3 lightDir = normalize(light.position - vertexPosition);

	float theta = dot(light.direction, -lightDir);
	float epsilon = light.cutoff - light.outerCutoff;
	float coneFactor = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);

	vec3 ambient = light.ambient * baseColor;
	vec3 diffuse = coneFactor * light.diffuse * baseColor * max(dot(lightDir, normal), 0.0);
	vec3 specular = coneFactor * light.specular * specularMask * max(dot(viewDir, reflect(-lightDir, normal)), 0.0);

	return vec3(ambient + diffuse + specular);
}

还可以做个变体,解密灯的效果

vec3 lerp(vec3 min, vec3 max, float alpha){
    
    
	return min + alpha * (max - min);
}

vec3 Lighting_SpotLight(SpotLight light, vec3 normal, vec3 viewDir, vec3 baseColor, float specularMask, vec3 pattern){
    
    
	
	// ...
	diffuse = lerp(diffuse, pattern, coneFactor);

	return vec3(ambient + diffuse + specular);
}

请添加图片描述

多光源

每个光源都会对像素产生影响,所以对于每个像素来说,逐个光源根据不同的光源类型进行计算就好

void main()
{
    
    
	vec3 lighting_dir = Lighting_DirLight();
	vec3 lighting_point;
	for(int i; i < MAX_N_POINT_LIGHTS; i++){
    
    
		lighting_point += Lighting_PointLight();
	}
	vec3 lighting_spot = Lighting_SpotLight();
	
	fragColor = vec4(lighting_dir + lighting_point + Lighting_SpotLight, 1.0);
}

唯一比较麻烦的是点光源,有多个光源要设置,所以还是写了个类

class PointLight {
    
    
public:
	int Index;

	glm::vec3 Position;
	glm::vec3 Ambient;
	glm::vec3 Diffuse;
	glm::vec3 Specular;
	float Constant;
	float Linear;
	float Quadratic;

	PointLight(int index = 0, 
		glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), 
		glm::vec3 ambient = glm::vec3(0.03f, 0.03f, 0.03f),
		glm::vec3 diffuse = glm::vec3(0.1f, 0.1f, 0.1f), 
		glm::vec3 specular = glm::vec3(1.0, 1.0, 1.0),
		float constant = 1.0f, float linear = 0.09f, float quadratic = 0.032f);

	void SendDataToGPU(Shader shader);
};

// ...

void PointLight::SendDataToGPU(Shader shader) {
    
    

	std::string name = "pointLights[";
	name.append(std::to_string(Index));
	name.append("]");

	shader.setVec3(name + ".position", Position);
	shader.setVec3(name + ".ambient", Ambient);
	// ...
}

猜你喜欢

转载自blog.csdn.net/weixin_44045614/article/details/128063050