从零开始学习opengl-12.投光物

定向光

当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(Directional Light)。

太阳光就有此特征,所以我们只需要知道方向即可。

vec3 lightDir = normalize(-light.direction);

人们通常更习惯定义一个定向光作为一个全局方向,它从光源发出。所以我们必须对全局光的方向向量取反来改变它的方向;它现在是一个方向向量指向光源。同时,确保对向量进行标准化处理

同时,不要忘记定义光源的方向(注意,我们把方向定义为:从光源处发出的方向;在下面,你可以快速看到光的方向的指向):

GLint lightDirPos = glGetUniformLocation(lightingShader.Program, "light.direction");
glUniform3f(lightDirPos, -0.2f, -1.0f, -0.3f);

现象:通过定向光可以照亮整个场景。
在这里插入图片描述

点光源

之前的教程我们已经使用了(最简单的)点光。我们指定了一个光源以及其所在的位置,它从这个位置向所有方向发散光线。在大多数3D仿真场景中,我们更希望去模拟一个离光源越近越亮,越远的越暗,而不是照亮整个场景的光源。

衰减

随着光线穿越距离的变远使得亮度也相应地减少的现象,通常称之为衰减(Attenuation)
衰减值:在这里插入图片描述
在这里d代表片段到光源的距离。为了计算衰减值,我们定义3个(可配置)项:常数项Kc,一次项Kl和二次项Kq。

常数项通常是1.0,它的作用是保证分母永远不会比1小,因为它可以利用一定的距离增加亮度,这个结果不会影响到我们所寻找的。
一次项用于与距离值相乘,这会以线性的方式减少亮度。
二次项用于与距离的平方相乘,为光源设置一个亮度的二次递减。二次项在距离比较近的时候相比一次项会比一次项更小,但是当距离更远的时候比一次项更大

多种场景下各参数的表:
在这里插入图片描述
default.frag

#version 330 core
out vec4 color;
  
in vec3 Normal;  
in vec3 FragPos;  
in vec2 TexCoords; 

//uniform vec3 lightPos; 
//uniform vec3 lightColor;
//uniform vec3 objectColor;
uniform vec3 viewPos;

struct Material
{
    sampler2D diffuse;
    vec3 specular;
    float shininess;
};

uniform Material material;
struct Light
{
    vec3 position;
	//vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
	float constant;
	float linear;
	float quadratic;
};
uniform Light light;
void main()
{
    // 环境光
    vec3 ambient =  light.ambient * vec3(texture(material.diffuse,TexCoords));

    // 漫反射光
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position-FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse,TexCoords));

    // 镜面高光
    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);  

	float distance1=length(light.position-FragPos);//求向量长度
	float attenuation=1.0f/(light.constant+light.linear*distance1+light.quadratic*(distance1*distance1));
    
	ambient*=attenuation;
	diffuse*=attenuation;
	specular*=attenuation;
	vec3 result = ambient + diffuse + specular;
    color = vec4(result, 1.0f);

	
} 
glUniform1f(glGetUniformLocation(lightingShader.Program, "light.constant"), 1.0f);
glUniform1f(glGetUniformLocation(lightingShader.Program, "light.linear"), 0.09);
glUniform1f(glGetUniformLocation(lightingShader.Program, "light.quadratic"), 0.032);

聚光

类似与手电筒。
在这里插入图片描述
LightDir:从片段指向光源的向量。
SpotDir:聚光所指向的方向。
Phiϕ:定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
Thetaθ:LightDir向量和SpotDir向量之间的角度。θ值应该比Φ值小,这样才会在聚光内。

我们需要光源的位置,方向和切光角。

glUniform3f(lightPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
glUniform3f(lightSpotdirLoc, camera.Front.x, camera.Front.y, camera.Front.z);
glUniform1f(lightSpotCutOffLoc, glm::cos(glm::radians(12.5f)));

上面为什么用cos?
我们计算LightDir和SpotDir向量的点乘,而点乘返回一个余弦值,不是一个角度,节约性能

现在剩下要做的是计算θ值,用它和ϕ值对比,以决定我们是否在或不在聚光的内部:

float theta = dot(lightDir, normalize(-light.direction));
if(theta > light.cutOff)
{
    // 执行光照计算
}
else // 否则使用环境光,使得场景不至于完全黑暗
color = vec4(light.ambient*vec3(texture(material.diffuse,TexCoords)), 1.0f);

默认光的方向是从光源作为指向出发点,而我们需要指向光源的方向所以取反,跟片断指向光源的向量点乘,然后角度小于切光角,则照亮,
但cos在0到90度,度数越大,cos值越小,所以用大于号,小于切光角则只有环境光

边缘软化/平滑

为创建聚光的平滑边,我们希望去模拟的聚光有一个内圆锥和外圆锥。我们可以把内圆锥设置为前面部分定义的圆锥,我们希望外圆锥从内边到外边逐步的变暗。

为创建外圆锥,我们简单定义另一个余弦值,它代表聚光的方向向量和外圆锥的向量(等于它的半径)的角度。然后,如果片段在内圆锥和外圆锥之间,就会给它计算出一个0.0到1.0之间的亮度。如果片段在内圆锥以内这个亮度就等于1.0,如果在外面就是0.0。

我们可以使用下面的公式计算这样的值:在这里插入图片描述
这里ϵ是内部(ϕ)和外部圆锥(γ)(\epsilon = \phi - \gamma)的差,
分子被减数即切光角的cos值
在这里插入图片描述
当在聚光外的时候是个负的,当在内部圆锥以内大于1。如果我们适当地把这个值固定,我们在片段着色器中就再不需要if-else了,

float theta = dot(lightDir, normalize(-light.direction));
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);
...
// We’ll leave ambient unaffected so we always have a little
light.diffuse* = intensity;
specular* = intensity;
...

现象:在这里插入图片描述
default.frag

#version 330 core
out vec4 color;
  
in vec3 Normal;  
in vec3 FragPos;  
in vec2 TexCoords; 

//uniform vec3 lightPos; 
//uniform vec3 lightColor;
//uniform vec3 objectColor;
uniform vec3 viewPos;

struct Material
{
    sampler2D diffuse;
    vec3 specular;
    float shininess;
};

uniform Material material;
struct Light
{
    vec3 position;
	vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

	float cutOff;//内圆锥的向量
	float outerCutOff;//外圆锥的向量
	float constant;
	float linear;
	float quadratic;
};
uniform Light light;
void main()
{
	//切光角
	vec3 lightDir = normalize(light.position-FragPos);
	float theta=dot(lightDir,normalize(-light.direction));
	//平滑参数
	float epsilon = light.cutOff - light.outerCutOff;
	float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);
	
	
		// 环境光
    vec3 ambient =  light.ambient * vec3(texture(material.diffuse,TexCoords));

    // 漫反射光
    vec3 norm = normalize(Normal);
    
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse =light.diffuse * diff * vec3(texture(material.diffuse,TexCoords));

    // 镜面高光
    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);  

	float distance1=length(light.position-FragPos);
	float attenuation=1.0f/(light.constant+light.linear*distance1+light.quadratic*(distance1*distance1));
    
	//ambient*=attenuation;
	diffuse*=attenuation;
	specular*=attenuation;
	
	diffuse*=intensity;
	specular*=intensity;
	
	vec3 result = ambient + diffuse + specular;
    color = vec4(result, 1.0f);	
	//color=vec4( light.ambient * vec3(texture(material.diffuse,TexCoords)),1.0f);
	

	
} 

shader.cpp(跟frag有关的代码)

ourShader.Use();
		//GLint objectColorLoc = glGetUniformLocation(ourShader.Program, "objectColor");
		//GLint lightColorLoc = glGetUniformLocation(ourShader.Program, "lightColor");
		GLint lightPosLoc = glGetUniformLocation(ourShader.Program, "light.position");
		GLint viewPosLoc = glGetUniformLocation(ourShader.Program, "viewPos");
		GLint lightSpotdirLoc = glGetUniformLocation(ourShader.Program, "light.direction");
		GLint lightSpotCutOffLoc = glGetUniformLocation(ourShader.Program, "light.cutOff");
		GLint lightSpotOutCutOffLoc = glGetUniformLocation(ourShader.Program, "light.outerCutOff");
		//glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);
		//glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f);

		glUniform3f(lightPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
		glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
		glUniform3f(lightSpotdirLoc, camera.Front.x, camera.Front.y, camera.Front.z);
		glUniform1f(lightSpotCutOffLoc, glm::cos(glm::radians(12.5f)));
		glUniform1f(lightSpotOutCutOffLoc, glm::cos(glm::radians(17.5f)));
		//GLint matAmbientLoc = glGetUniformLocation(ourShader.Program, "material.ambient");
		//GLint matDiffuseLoc = glGetUniformLocation(ourShader.Program, "material.diffuse");
		GLint matSpecularLoc = glGetUniformLocation(ourShader.Program, "material.specular");
		GLint matShineLoc = glGetUniformLocation(ourShader.Program, "material.shininess");

		//glUniform3f(matAmbientLoc, 1.0f, 0.5f, 0.31f);
		//glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
		glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
		glUniform1f(matShineLoc, 32.0f);
		GLint lightAmbientLoc = glGetUniformLocation(ourShader.Program, "light.ambient");
		GLint lightDiffuseLoc = glGetUniformLocation(ourShader.Program, "light.diffuse");
		GLint lightSpecularLoc = glGetUniformLocation(ourShader.Program, "light.specular");

		glUniform3f(lightAmbientLoc, 0.1f, 0.1f, 0.1f);
		glUniform3f(lightDiffuseLoc, 0.8f, 0.8f, 0.8f);// 让我们把这个光调暗一点,这样会看起来更自然
		glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
		glUniform1f(glGetUniformLocation(ourShader.Program, "light.constant"), 1.0f);
		glUniform1f(glGetUniformLocation(ourShader.Program, "light.linear"), 0.09);
		glUniform1f(glGetUniformLocation(ourShader.Program, "light.quadratic"), 0.032);
		// Create camera transformations
		glm::mat4 view;
		view = camera.GetViewMatrix();
		glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
		// Get the uniform locations
		GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
		GLint viewLoc = glGetUniformLocation(lightingShader.Program, "view");
		GLint projLoc = glGetUniformLocation(lightingShader.Program, "projection");
		// Pass the matrices to the shader
		glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
		glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Bind diffuse map
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, diffuseMap);
		
		// Draw the container (using container's vertex attributes)
		glm::mat4 model;
		glBindVertexArray(containerVAO);
		for (GLuint i = 0; i < 10; i++)
		{
			model = glm::mat4();
			model = glm::translate(model, cubePositions[i]);
			GLfloat angle = 20.0f * i;
			model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
			glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

			glDrawArrays(GL_TRIANGLES, 0, 36);
		}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值