光照越远,衰减越厉害
衰减算法:
常数项通常是1.0,它的作用是保证分母永远不会比1小,因为它可以利用一定的距离增加亮度,这个结果不会影响到我们所寻找的。
一次项用于与距离值相乘,这会以线性的方式减少亮度。
二次项用于与距离的平方相乘,为光源设置一个亮度的二次递减。二次项在距离比较近的时候相比一次项会比一次项更小,但是当距离更远的时候比一次项更大。
柔光算法:
内圆亮度都是一样
内圆到外圆,逐渐变暗,即形成了柔和的光照边缘
main.cpp
//main.cpp
#include <string>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "Shader.h"
#include "Camera.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <SOIL/SOIL.h>
#include <iostream>
#include "Shader.h"
#pragma comment(lib, "./SOIL.lib")
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glew32s.lib")
#pragma comment (lib, "glfw3.lib")
#pragma comment (lib, "glfw3dll.lib")
#pragma comment (lib, "glew32mxs.lib")
void key_callback(GLFWwindow* pWnd, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* pWnd, double xpos, double ypos);
void scroll_callback(GLFWwindow* pWnd, double xoffset, double yoffset);
void do_movement();
const GLuint WIDTH = 800, HEIGHT = 600;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
GLfloat lastX = WIDTH / 2.0;
GLfloat lastY = HEIGHT / 2.0;
bool keys[1024];
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
/
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* pWnd = glfwCreateWindow(WIDTH, HEIGHT, "LightBase", nullptr, nullptr);
glfwMakeContextCurrent(pWnd);
glfwSetKeyCallback(pWnd, key_callback);
glfwSetCursorPosCallback(pWnd, mouse_callback);
glfwSetScrollCallback(pWnd, scroll_callback);
// 鼠标指针隐藏
//glfwSetInputMode(pWnd, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glewExperimental = GL_TRUE;
glewInit();
glViewport(0, 0, WIDTH, HEIGHT);
glEnable(GL_DEPTH_TEST);
// Shader 处理 箱子 以及 灯光
Shader lightingObjShader("./obj_vertex", "./obj_fragement");
Shader lampShader("./lamp_vertex", "./lamp_fragement");
// 点数据
GLfloat vertices[] = { // 位置3 向量3 纹理2
// 背面
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
// 前面
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
// 左面
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
// 右面
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
// 下面
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
// 上面
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
// 箱子位置
glm::vec3 objPos[] = {
glm::vec3(-3.0f, -1.0f, -5.0f), glm::vec3(2.0f, 5.0f, -1.0f),
glm::vec3(-1.5f, -2.2f, -2.5f), glm::vec3(0.0f, -2.0f, -5.3f),
glm::vec3(2.4f, -0.4f, -3.5f), glm::vec3(-1.7f, 0.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f), glm::vec3(1.5f, 2.0f, -2.5f),
glm::vec3(1.5f, 0.2f, -1.5f), glm::vec3(-1.3f, 1.0f, -1.5f) };
GLuint objVAO, VBO;
glGenVertexArrays(1, &objVAO);
// 光照物体
glBindVertexArray(objVAO); // --- Begin
{
// 顶点数据复至GPU中
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
{
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 位置索引 传至Shader的
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// 向量索引
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
// 纹理索引
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glBindVertexArray(0); // --- End
// 灯泡 (只需要用到一部分坐标点数据,用于绘制一个立方体即可)
GLuint lampVAO, lampVBO;
glGenVertexArrays(1, &lampVAO);
glBindVertexArray(lampVAO);
{
//glGenBuffers(1, &lampVBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
{
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glBindVertexArray(0);
// 纹理1 木箱纹理贴图 --
// 1. 加载图片 木箱图片
int nTextureW = 0, nTextureH = 0;
unsigned char* pChImg = nullptr;
pChImg = SOIL_load_image("texture.png", &nTextureW, &nTextureH, 0, SOIL_LOAD_RGB);
// 2. 纹理设置
GLuint diffuseMap;
glGenTextures(1, &diffuseMap);
{
glBindTexture(GL_TEXTURE_2D, diffuseMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, nTextureW, nTextureH, 0, GL_RGB, GL_UNSIGNED_BYTE, pChImg);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST);
}
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(pChImg); // 释放内存
// 纹理2 木箱边框反射贴图 --
// 1. 加载图片 木箱边框图片
pChImg = SOIL_load_image("texture_specular.png", &nTextureW, &nTextureH, 0, SOIL_LOAD_RGB);
// 2. 纹理设置
GLuint specularMap;
glGenTextures(1, &specularMap);
glBindTexture(GL_TEXTURE_2D, specularMap);
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, nTextureW, nTextureH, 0, GL_RGB, GL_UNSIGNED_BYTE, pChImg);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST);
}
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(pChImg);
// 设置Shader中的材质属性
lightingObjShader.useShaderPrograme();
glUniform1i(glGetUniformLocation(lightingObjShader.getPrograme(), "material.diffuse"), 0);
glUniform1i(glGetUniformLocation(lightingObjShader.getPrograme(), "material.specular"), 1);
GLuint nCurrentTime = glfwGetTime();
GLuint nLastTime = glfwGetTime();
GLuint nFPS = 0;
glm::mat4 view;
glm::mat4 model;
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // 只绘边框
while (!glfwWindowShouldClose(pWnd))
{
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glfwPollEvents();
do_movement();
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
lightingObjShader.useShaderPrograme();
// 设置光源参数
GLint lightPosLoc = glGetUniformLocation(lightingObjShader.getPrograme(), "light.position");
GLint lightDirLoc = glGetUniformLocation(lightingObjShader.getPrograme(), "light.direction");
GLint lightPosCutoffLoc = glGetUniformLocation(lightingObjShader.getPrograme(), "light.cutOff");
GLint lightSpotOuterCutOffLoc = glGetUniformLocation(lightingObjShader.getPrograme(), "light.outerCutOff"); // add 外圆
GLint viewPosLoc = glGetUniformLocation(lightingObjShader.getPrograme(), "viewPos");
glUniform3f(lightPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
glUniform3f(lightDirLoc, camera.Front.x, camera.Front.y, camera.Front.z);
glUniform1f(lightPosCutoffLoc, glm::cos(glm::radians(12.5))); // 聚光灯半径 内圆
glUniform1f(lightSpotOuterCutOffLoc, glm::cos(glm::radians(17.5f))); // add 聚光灯 外圆 内圆至外圆逐渐变暗
glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
// 设置Shader中的灯光属性
glUniform3f(glGetUniformLocation(lightingObjShader.getPrograme(), "light.ambient"), 0.1f, 0.1f, 0.1f);
glUniform3f(glGetUniformLocation(lightingObjShader.getPrograme(), "light.diffuse"), 0.8f, 0.8f, 0.8f);
glUniform3f(glGetUniformLocation(lightingObjShader.getPrograme(), "light.specular"), 1.0f, 1.0f, 1.0f);
glUniform1f(glGetUniformLocation(lightingObjShader.getPrograme(), "light.constant"), 1.0f);
glUniform1f(glGetUniformLocation(lightingObjShader.getPrograme(), "light.linear"), 0.09f);
glUniform1f(glGetUniformLocation(lightingObjShader.getPrograme(), "light.quadratic"), 0.032f);
// 设置Shader中的材质属性
glUniform1f(glGetUniformLocation(lightingObjShader.getPrograme(), "material.shininess"), 4.0f);
// 摄像机设置
view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
GLint modelLoc = glGetUniformLocation(lightingObjShader.getPrograme(), "model");
GLint viewLoc = glGetUniformLocation(lightingObjShader.getPrograme(), "view");
GLint projLoc = glGetUniformLocation(lightingObjShader.getPrograme(), "projection");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// 绑定漫反射贴图
glActiveTexture(GL_TEXTURE0); // 箱子纹理
glBindTexture(GL_TEXTURE_2D, diffuseMap);
glActiveTexture(GL_TEXTURE1); // 箱子边框的镜面反射纹理
glBindTexture(GL_TEXTURE_2D, specularMap);
// 绘制箱子 -------------------
glBindVertexArray(objVAO); // 绑定箱子的顶点数据进行操作
for (GLuint i = 0; i < 10; i++)
{
model = glm::mat4();
model = glm::translate(model, objPos[i]);
GLfloat angle = glfwGetTime();
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);
}
for (GLuint i = 0; i < 10; i++)
{
model = glm::mat4();
model = glm::translate(model, objPos[i] + objPos[i]);
GLfloat angle = glfwGetTime();
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);
}
for (GLuint i = 0; i < 10; i++)
{
model = glm::mat4();
model = glm::translate(model, objPos[i] + objPos[i] + objPos[i]);
GLfloat angle = glfwGetTime();
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);
}
glBindVertexArray(0); // 解绑
// 绘制灯泡 -------------------
lampShader.useShaderPrograme();
modelLoc = glGetUniformLocation(lampShader.getPrograme(), "model");
viewLoc = glGetUniformLocation(lampShader.getPrograme(), "view");
projLoc = glGetUniformLocation(lampShader.getPrograme(), "projection");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
model = glm::mat4();
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f)); // 缩小
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(lampVAO); // 绑定灯泡的顶点数据进行操作
{
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glBindVertexArray(0); // 解绑
glfwSwapBuffers(pWnd);
// -----------------------------------------------
nLastTime = glfwGetTime();
nFPS++;
if (nLastTime - nCurrentTime > 1)
{
std::cout << "当前帧率:" << nFPS << std::endl;
nFPS = 0;
nCurrentTime = nLastTime;
}
}
glfwTerminate();
return 0;
}
void key_callback(GLFWwindow* pWnd, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(pWnd, GL_TRUE);
if (key >= 0 && key < 1024)
{
if (action == GLFW_PRESS)
keys[key] = true;
else if (action == GLFW_RELEASE)
keys[key] = false;
}
}
void do_movement()
{
if (keys[GLFW_KEY_W])
camera.ProcessKeyboard(FORWARD, deltaTime);
if (keys[GLFW_KEY_S])
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (keys[GLFW_KEY_A])
camera.ProcessKeyboard(LEFT, deltaTime);
if (keys[GLFW_KEY_D])
camera.ProcessKeyboard(RIGHT, deltaTime);
}
bool firstMouse = true;
void mouse_callback(GLFWwindow* pWnd, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
GLfloat xoffset = xpos - lastX;
GLfloat yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
//camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_callback(GLFWwindow* pWnd, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
Shader.h Camera.h 以及Shader等参照 http://blog.csdn.net/yulinxx/article/details/72720944
obj_fragement 稍有修改:
// 箱子 片断着色器
#version 330 core
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
// 材质结构体
struct Material
{
sampler2D diffuse; // 漫反射光照下物体的颜色
sampler2D specular; // 物体受到的镜面光照的影响的颜色
float shininess; // 高光的散射/半径
};
struct Light {
vec3 position; // 光源位置
vec3 direction;
vec3 ambient; // 环境光
vec3 diffuse; // 漫反射光
vec3 specular; // 镜面光
// 计算衰减值
float constant; // 常数项Kc
float linear; // 一次项Kl
float quadratic; // 二次项KqKq
float cutOff; // 内圆切光角
float outerCutOff; // add 外圆切光角
};
in vec3 IN_ObjPos; // 顶点位置
in vec3 IN_Normal; // 法向量
in vec2 IN_TexCoords; // 纹理
out vec4 color;
uniform vec3 viewPos;
uniform Material material;
uniform Light light;
void main()
{
// 环境光
vec3 ambient = light.ambient * vec3(texture(material.diffuse, IN_TexCoords));
// 漫反射
vec3 norm = normalize(IN_Normal); // 标准化 只关心方向
vec3 lightDir = normalize(light.position - IN_ObjPos); // 光源和片段位置之间的方向向量
//vec3 lightDir = normalize(-light.direction);
float diff = max(dot(norm, lightDir), 0.0); // 点乘, 来计算光对当前片段的实际的散射影响 大于90度, 点乘的结果就会变成负数, max 函数返回两个参数之间较大的参数, 从而保证散射因子不会变成负数。
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, IN_TexCoords));
// 镜面光
vec3 reflectDir = reflect(-lightDir, norm); // 计算反射向量和视线方向的角度, 如果之间的角度越小, 那么镜面光的作用就会越大
vec3 viewDir = normalize(viewPos - IN_ObjPos); // 观察者世界空间位置(Viewer’s WorldSpace Position) 到片段的位置 的向量
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * spec * vec3(texture(material.specular, IN_TexCoords));
// 柔边缘算法 Spotlight (soft edges)
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = (light.cutOff - light.outerCutOff); // 内外角之差 相差越大,
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); // 把第一个参数固定在0.0和1.0之间
diffuse *= intensity;
specular *= intensity;
//vec3 specular = vec3(0.0, 0.0, 0.0);
// Attenuation 衰减
float distance = length(light.position - IN_ObjPos); // 光源到物体的距离
float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); // 衰减因子
ambient *= attenuation; // 所有的光线都与衰减因子相乘
diffuse *= attenuation;
specular *= attenuation;
color = vec4(ambient + diffuse + specular, 1.0f); // 最后的输出颜色。
}