【OpenGL】基础光照

news2024/11/20 2:24:44

介绍

现实世界中的光照是极其复杂,难以计算的,因此OpenGL的光照使用的是简化的模型,其中一个模型被称为冯氏光照模型(Phong Lighting Model)。

冯氏光照模型的主要结构由三个分量组成:

  1. 环境(Ambient)光照
  2. 漫反射(Diffuse)光照
  3. 镜面(Specular)光照

环境光照

光的一大特点是,它可以向很多方向发散并反弹,所以现实环境周围通常总会有些光亮,对应的物体通常都会反射些微弱的光。
计算:

用一个很小的分量乘以光的颜色,最后乘以物体的颜色

片段着色器:

void main()
{
    //计算环境光照
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;
    //片段最终颜色:乘以物体本身颜色
    vec3 result = ambient * objectColor;   
    FragColor = vec4(result, 1.0);
}

效果
image.png
(完整代码会在最后放出,先只介绍关键代码)

漫反射光照

漫反射光照使物体与光线方向越接近的片段能充光源处获得更多的亮度。

image.png

如果光线垂直于物体表面,这束光对物体的影响会最大化(译注:更亮)。

可以大致理解为

片段的漫反射光照 = (光源坐标到片段坐标的向量与片段法向量的夹角) * 光源颜色

注意两个向量都要先归一化
为了计算漫反射光照我们需要知道这几个值:

  • 片段所在面的法向量
  • 光源的世界坐标
  • 片段的世界坐标

最后目前片段的颜色为: (环境光照值+漫反射光照值) * 片段自身颜色

光照的计算我们都放在片段着色器中来处理,所以在物体的顶点着色器我们把需要的变量都传递给片段着色器:

#version 330 core
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 nor;

//光照计算统一传入到片段着色器中计算
out vec3 normal;
out vec4 fragPos;		//片段世界坐标

uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projectionMat;

void main(){
	gl_Position = projectionMat * viewMat * modelMat * vec4(pos, 1.0f);
	normal = nor;
	fragPos = modelMat * vec4(pos, 1.0);		//顶点坐标乘以模型矩阵转为世界坐标
}

片段着色器部分代码:

//计算漫反射光照
//转为单位向量
vec3 norm = normalize(normal);
vec3 lightDir = normalize(lightPos - vec3(fragPos));
float rad = max(dot(lightDir, norm), 0.0);			//光照方向与片段法向量夹角[0,PI/2]
vec3 diffuse = rad * lightColor;							//漫反射光照

vec3 result = (ambient + diffuse)  * cubeColor;
fragColor = vec4(result, 1.0);

效果:

image.png

镜面光照

镜面光照决定于表面的反射特性。如果我们把物体表面设想为一面镜子,那么镜面光照最强的地方就是我们看到表面上反射光的地方。如下图:

image.png

为此我们还需要一个观察者坐标(即摄像机)。光的反射向量与观察方向之间夹角越小,镜面光照效果越强。
物体片段着色器部分代码:

//计算镜面光照
vec3 viewDir = normalize(viewPos - vec3(fragPos));			//观察者方向
vec3 reflectDir = reflect(-lightDir, norm);							//光照经法向量折射方向
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);	//反光度,这里取点乘的32次幂
float specularStrength = 0.5;												//镜面强度
vec3 specular = spec * specularStrength * lightColor;		//镜面光照

vec3 result = (ambient + diffuse + specular)  * cubeColor;
fragColor = vec4(result, 1.0);

效果
image.png

距离对光照强度影响

如果我们让光源环绕物体,会看见每个面总是整面同时亮起,除了镜面光照外几乎没有色差:
GIF.gif

这是因为目前光照强度永远为1,与距离无关。
我们可以给光源设定有效距离,且在这个距离内逐渐衰弱。
用片段坐标-光源坐标得到的向量再求模长,就是光源到片段的距离了,再根据这个距离衰减光照强度即可。(当然这只是简单的计算方式,具体的计算方式会更复杂)
物体的片段着色器:

//距离影响光照强度,简化的计算方式
float enableDistance = 3.0f;	//有效光照距离
vec3 light2fragVec = lightPos - vec3(fragPos);
float distan = min(sqrt(light2fragVec.x * light2fragVec.x + light2fragVec.y * light2fragVec.y + light2fragVec.z * light2fragVec.z), enableDistance);
float strength = 1 - distan / enableDistance;

//光照远近先不影响环境光照了
vec3 result = ambient * cubeColor + (diffuse + specular)  * cubeColor * 1;
fragColor = vec4(result, 1.0);

现在的效果:
final.gif

完整代码

basicLightingMain.cpp

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "../Util/MyOpenGLUtil.h"
#include "../Util/Shader.h"
#include "../Util/Camera.h"

GLFWwindow* window = nullptr;
int width = 800;
int height = 600;

float deltaTime = 0;
float lastTime = 0;
float mouseX = 0.0f, mouseY = 0.0f;			//鼠标坐标
float lastX = 0.0f, lastY = 0.0f;						//上一帧的位置
float scrollYOffset = 0.0f;								//滚轮未使用的偏移

glm::vec3 lightPos = glm::vec3(2.0, 0, 0);				//光源坐标
glm::vec3 cameraBasePos = glm::vec3(0.7, 1.0, 5.0);			//摄像机初始坐标

	//创建摄像机
Camera camera(cameraBasePos);

void Work1();			//一个白光光源,一个被照射的物体进行颜色相乘计算出反射颜色
void mouse_callback(GLFWwindow* window, double xPos, double yPos);			//鼠标移动回调函数
void scroll_callback(GLFWwindow* window, double xOffset, double yOffset);		//鼠标滚轮回调函数

int main() {
	Init(&window, &width, &height);
	Work1();

	return 0;
}

void Work1() {
	//创建顶点缓冲对象
	unsigned int VBO;
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);		//绑定缓冲对象,设定缓冲类型
	//将顶点数据复制到缓冲的内存中,并指定显卡如何管理数据,这里指定为GL_STATIC_DRAW
	//使用带法向量的顶点数组(为每个顶点手动配置法向量)
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_cubeHaveNormal), vertices_cubeHaveNormal, GL_STATIC_DRAW);

	//创建VAO顶点数组对象
	unsigned int VAO;
	glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);
	//顶点
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);		//以顶点属性位置作为参数,启用顶点属性
	//法向量
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(sizeof(float) * 3));
	glEnableVertexAttribArray(1);		//以顶点属性位置作为参数,启用顶点属性

	//创建light的VAO
	unsigned int lightVAO;
	glGenVertexArrays(1, &lightVAO);
	glBindVertexArray(lightVAO);
	//顶点
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);		//以顶点属性位置作为参数,启用顶点属性
	glBindBuffer(GL_ARRAY_BUFFER, VBO);		//绑定缓冲对象,设定缓冲类型

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	Shader cubeShader("cubew1.vert", "cubew1.frag");
	cubeShader.use();
	//创建模型矩阵,包含物体的位移、旋转、缩放的变换信息
	glm::mat4 modelMat, viewMat, projectionMat;
	//开启深度测试
	glEnable(GL_DEPTH_TEST);
	float xOffset = 0.0f, yOffset = 0.0f;			//鼠标本次偏移


	Shader lightShader("cubew1.vert", "lightw1.frag");
	//隐藏并捕获鼠标,且让鼠标保持在窗口内
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
	//当鼠标移动时,回调函数
	glfwSetCursorPosCallback(window, mouse_callback);
	//注册鼠标滚轮回调函数
	glfwSetScrollCallback(window, scroll_callback);

	while (glfwWindowShouldClose(window) == false) {
		//数值刷新
		float curTime = glfwGetTime();
		deltaTime = curTime - lastTime;
		lastTime = curTime;

		//让光源绕原点旋转
		float distance = 2.0f;
		float lightY = sin(glfwGetTime()) * distance;
		float lightZ = -(cos(glfwGetTime()) * distance);
		float lightX = sin(glfwGetTime()) * distance; 
		//lightPos.x = lightX; lightPos.y = 1; lightPos.z = 1.2;						//直线移动
		lightPos.x = lightX; lightPos.y = lightY; lightPos.z = lightZ;			//环绕移动


		processInput(window);
		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		//清除上一帧的颜色缓冲和深度缓冲
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		cubeShader.use();
		//刷新摄像头
		xOffset = mouseX - lastX; yOffset = lastY - mouseY;
		lastX = mouseX; lastY = mouseY;
		camera.ProcessMouseMovement(xOffset, yOffset);
		camera.ProcessMouseScroll(scrollYOffset);
		scrollYOffset = 0;
		//按键事件
		if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
			camera.ProcessKeyboard(FORWARD, deltaTime);
		}
		if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
			camera.ProcessKeyboard(BACKWARD, deltaTime);
		}
		if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
			camera.ProcessKeyboard(RIGHT, deltaTime);
		}
		if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
			camera.ProcessKeyboard(LEFT, deltaTime);
		}

		modelMat = glm::mat4(1.0f);
		viewMat = camera.GetViewMatrix();
		projectionMat = glm::mat4(1.0f);
		projectionMat = glm::perspective(glm::radians(45.0f), width * 1.0f / height, 0.1f, 100.0f);


		unsigned int modelLoc = glGetUniformLocation(cubeShader.ID, "modelMat");
		glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMat));
		unsigned int viewLoc = glGetUniformLocation(cubeShader.ID, "viewMat");
		glUniformMatrix4fv(viewLoc, 1, GL_FALSE, &viewMat[0][0]);
		unsigned int projectionLoc = glGetUniformLocation(cubeShader.ID, "projectionMat");
		glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projectionMat));
		//设置cube颜色
		cubeShader.set3Float("cubeColor", 1.0f, 0.5f, 0.31f);
		//设置光源颜色
		cubeShader.set3Float("lightColor", 1.0f, 1.0f, 1.0f);
		//设置光源世界坐标
		cubeShader.set3Float("lightPos", lightPos.x, lightPos.y, lightPos.z);
		//glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
		//设置观察者(摄像机)坐标
		cubeShader.set3Float("viewPos", camera.Position.x, camera.Position.y, camera.Position.z);
		//printf("camera.Position: %5.5lf %5.5lf %5.5lf\n", camera.Position.x, camera.Position.y, camera.Position.z);

		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		//光源shader
		lightShader.use();
		modelMat = glm::mat4(1.0f);
		modelMat = glm::translate(modelMat, lightPos);
		modelMat = glm::scale(modelMat, glm::vec3(0.2, 0.2, 0.2));
		viewMat = camera.GetViewMatrix();
		projectionMat = glm::mat4(1.0f);
		projectionMat = glm::perspective(glm::radians(45.0f), width * 1.0f / height, 0.1f, 100.0f);
		modelLoc = glGetUniformLocation(lightShader.ID, "modelMat");
		glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMat));
		viewLoc = glGetUniformLocation(lightShader.ID, "viewMat");
		glUniformMatrix4fv(viewLoc, 1, GL_FALSE, &viewMat[0][0]);
		projectionLoc = glGetUniformLocation(lightShader.ID, "projectionMat");
		glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projectionMat));

		//设置光源颜色
		lightShader.set3Float("lightColor", 1.0f, 1.0f, 1.0f);
		glBindVertexArray(lightVAO);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		glfwSwapBuffers(window);

		glfwPollEvents();			//获取键盘输入
	}
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glfwTerminate();
}

void mouse_callback(GLFWwindow* window, double xPos, double yPos) {
	static bool firstMouse = true;			//第一次进入窗口,会发生较大移动,这个移动要会忽略掉
	if (firstMouse) {
		lastX = xPos;
		lastY = yPos;
		firstMouse = false;
	}
	::mouseX = xPos;
	::mouseY = yPos;
	//printf("mouseX: %5lfmouseY: %5lf\n", xPos, yPos);
}

void scroll_callback(GLFWwindow* window, double xOffset, double yOffset) {
	scrollYOffset += (float)yOffset;
}

MyOpenGLUtil.h

#pragma once
#define GLEW_STATIC
#include<GL/glew.h>
#include<GLFW/glfw3.h>

static int* widthPtr = nullptr, * heightPtr = nullptr;

void processInput(GLFWwindow* window);			//按键事件处理
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void Init(GLFWwindow** window, int width = 800, int height = 600);																//初始化
void Init(GLFWwindow** window, int* width, int* height);
unsigned int CreateShader(const int shaderType, const char* shaderSource);
unsigned int CreateShaderProgram(unsigned int vertexShader, unsigned int fragmentShader);

static float vertices_cubeHaveTexture[] = {
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
		 0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		 0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		 0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		 0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		 0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
		 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
};

static float vertices_cubeHaveNormal[] = {
		-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
	 0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
	 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
	 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
	-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
	-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

	-0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
	 0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
	 0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
	 0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
	-0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
	-0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,

	-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
	-0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
	-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
	-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
	-0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
	-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

	 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
	 0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
	 0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
	 0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
	 0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
	 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

	-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
	 0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
	 0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
	 0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
	-0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
	-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

	-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
	 0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
	 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
	 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
	-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
	-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
};

MyOpenGLUtil.cpp

#include<iostream>
#include "MyOpenGLUtil.h"
using namespace std;

void processInput(GLFWwindow* window) {
	//当按下指定键时,关闭窗口
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}
}

//初始化
void Init(GLFWwindow** mainWindow, int width, int height) {
	glfwInit();				//初始化
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);					//使用主版本号3
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);					//使用次版本号3
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);			//使用核心模式配置
#ifdef __APPLE__
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
	//Open GLFW Window  开一个窗口
	* mainWindow = glfwCreateWindow(width, height, "My OpenGL Game", NULL, NULL);
	if (*mainWindow == NULL) {
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();				//终止
		exit(-1);
	}
	glfwMakeContextCurrent(*mainWindow);
	glfwSetFramebufferSizeCallback(*mainWindow, framebuffer_size_callback);

	//glad使用这个加载函数
	// glad: load all OpenGL function pointers
	//if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	//{
	//	std::cout << "Failed to initialize GLAD" << std::endl;
	//	exit(-1);
	//}

	//Init GLFW
	glewExperimental = true;
	if (glewInit() != GLEW_OK) {
		cout << "Init GLFW failed." << endl;
		glfwTerminate();
		exit(1);
	}
}

void Init(GLFWwindow** mainWindow, int* width, int* height) {
	widthPtr = width; heightPtr = height;
	Init(mainWindow, *widthPtr, *heightPtr);
}

unsigned int CreateShader(const int shaderType, const char* shaderSource) {
	unsigned int shader;
	shader = glCreateShader(shaderType);
	glShaderSource(shader, 1, &shaderSource, NULL);
	glCompileShader(shader);
	int success;
	char infoLog[512];
	glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(shader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog << std::endl;
	}
	return shader;
}

unsigned int CreateShaderProgram(unsigned int vertexShader, unsigned int fragmentShader) {
	unsigned int shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);
	int success;
	char infoLog[512];
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::SHADERPROGRAM::LINK_FAILED\n" << infoLog << std::endl;
	}
	return shaderProgram;
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	cout << width << "   " << height << endl;
	if (widthPtr && heightPtr) {
		*widthPtr = width; *heightPtr = height;
	}
	// make sure the viewport matches the new window dimensions; note that width and 
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}

Shader.h

#pragma once
#ifndef SHADER
#define SHADER

#define GLEW_STATIC
#include <GL/glew.h>; // 包含glew来获取所有的必须OpenGL头文件
#include <GLFW/glfw3.h>;
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

class Shader
{
public:
    // 程序ID
    unsigned int ID;

    // 构造器读取并构建着色器
    Shader(const char* vertexPath, const char* fragmentPath);
    // 使用/激活程序
    void use();
    // uniform工具函数
    void setBool(const std::string& name, bool value) const;
    void setInt(const std::string& name, int value) const;
    void setFloat(const std::string& name, float value) const;
    void set3Float(const std::string& name, float f1, float f2, float f3) const;
    void set4Float(const std::string& name, float f1, float f2, float f3, float f4) const;
};
#endif // !SHADER

Shader.cpp

#include"Shader.h"

Shader::Shader(const char* vertexPath, const char* fragmentPath) {
	std::ifstream vFile(vertexPath), fFile(fragmentPath);
	std::stringstream vss, fss;
	std::string vertexShaderCodeStr, fragmentShaderCodeStr;

	//保证ifstream对象可以抛出异常
	vFile.exceptions(std::istream::failbit | std::istream::badbit);
	fFile.exceptions(std::istream::failbit | std::istream::badbit);
	try {
		if (vFile && fFile) {
			vss << vFile.rdbuf();
			fss << fFile.rdbuf();
			vertexShaderCodeStr = vss.str();
			fragmentShaderCodeStr = fss.str();
			vFile.close();
			fFile.close();
		}
	}
	catch (std::ifstream::failure e) {
		std::cerr << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
	}
	const char* vertexShaderCode = vertexShaderCodeStr.c_str(), * fragmentShaderCode = fragmentShaderCodeStr.c_str();

	//创建顶点着色器
	unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &vertexShaderCode, NULL);
	glCompileShader(vertexShader);
	int success;
	char infoLog[512];
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
		std::cout << "ERROR::VERTEX_SHADER::COMPILATION_FAILED\n" << infoLog << std::endl;
	}

	//创建片段着色器
	unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL);
	glCompileShader(fragmentShader);
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
		std::cout << "ERROR::FRAGMENT_SHADER::COMPILATION_FAILED\n" << infoLog << std::endl;
	}

	//创建Program
	unsigned int shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::SHADERPROGRAM::LINK_FAILED\n" << infoLog << std::endl;
	}
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

	ID = shaderProgram;
}
// 使用/激活程序
void Shader::use() {
	glUseProgram(ID);
}
// uniform工具函数
void Shader::setBool(const std::string& name, bool value) const {
	glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void Shader::setInt(const std::string& name, int value) const {
	glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
void Shader::setFloat(const std::string& name, float value) const {
	glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
void Shader::set3Float(const std::string& name, float f1, float f2, float f3) const {
	glUniform3f(glGetUniformLocation(ID, name.c_str()), f1, f2, f3);
}
void Shader::set4Float(const std::string& name, float f1, float f2, float f3, float f4) const {
	glUniform4f(glGetUniformLocation(ID, name.c_str()), f1, f2, f3, f4);
}

Camera.h

#ifndef CAMERA_H
#define CAMERA_H

#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <vector>

// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT
};

// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;


// An abstract camera class that processes input and calculates the corresponding Euler Angles, Vectors and Matrices for use in OpenGL
class Camera
{
public:
    // camera Attributes
    glm::vec3 Position;
    glm::vec3 Front;
    glm::vec3 Up;
    glm::vec3 Right;
    glm::vec3 WorldUp;
    // euler Angles
    float Yaw;
    float Pitch;
    // camera options
    float MovementSpeed;
    float MouseSensitivity;
    float Zoom;

    // constructor with vectors
    Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
    {
        Position = position;
        WorldUp = up;
        Yaw = yaw;
        Pitch = pitch;
        updateCameraVectors();
    }
    // constructor with scalar values
    Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
    {
        Position = glm::vec3(posX, posY, posZ);
        WorldUp = glm::vec3(upX, upY, upZ);
        Yaw = yaw;
        Pitch = pitch;
        updateCameraVectors();
    }

    // returns the view matrix calculated using Euler Angles and the LookAt Matrix
    glm::mat4 GetViewMatrix()
    {
        return glm::lookAt(Position, Position + Front, Up);
    }

    // processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
    void ProcessKeyboard(Camera_Movement direction, float deltaTime)
    {
        float velocity = MovementSpeed * deltaTime;
        if (direction == FORWARD)
            Position += Front * velocity;
        if (direction == BACKWARD)
            Position -= Front * velocity;
        if (direction == LEFT)
            Position -= Right * velocity;
        if (direction == RIGHT)
            Position += Right * velocity;
    }

    // processes input received from a mouse input system. Expects the offset value in both the x and y direction.
    void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
    {
        xoffset *= MouseSensitivity;
        yoffset *= MouseSensitivity;

        Yaw += xoffset;
        Pitch += yoffset;

        // make sure that when pitch is out of bounds, screen doesn't get flipped
        if (constrainPitch)
        {
            if (Pitch > 89.0f)
                Pitch = 89.0f;
            if (Pitch < -89.0f)
                Pitch = -89.0f;
        }

        // update Front, Right and Up Vectors using the updated Euler angles
        updateCameraVectors();
    }

    // processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
    void ProcessMouseScroll(float yoffset)
    {
        Zoom -= (float)yoffset;
        if (Zoom < 1.0f)
            Zoom = 1.0f;
        if (Zoom > 45.0f)
            Zoom = 45.0f;
    }

private:
    // calculates the front vector from the Camera's (updated) Euler Angles
    void updateCameraVectors()
    {
        // calculate the new Front vector
        glm::vec3 front;
        front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
        front.y = sin(glm::radians(Pitch));
        front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
        Front = glm::normalize(front);
        // also re-calculate the Right and Up vector
        Right = glm::normalize(glm::cross(Front, WorldUp));  // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
        Up = glm::normalize(glm::cross(Right, Front));
    }
};
#endif

其他没有的头文件是官网下的。

cubew1.vert (物体和光源共同使用该顶点着色器)

#version 330 core
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 nor;

//光照计算统一传入到片段着色器中计算
out vec3 normal;
out vec4 fragPos;		//片段世界坐标

uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projectionMat;

void main(){
	gl_Position = projectionMat * viewMat * modelMat * vec4(pos, 1.0f);
	normal = nor;
	fragPos = modelMat * vec4(pos, 1.0);		//顶点坐标乘以模型矩阵转为世界坐标
}

lightw1.frag

#version 330 core

out vec4 fragColor;

uniform vec3 lightColor;

void main(){
	fragColor = vec4(lightColor, 1.0);
}

cubew1.frag

#version 330 core
in vec3 normal;
in vec4 fragPos;						//片段世界坐标

out vec4 fragColor;

uniform vec3 cubeColor;
uniform vec3 lightColor;
uniform vec3 lightPos;			//光照世界坐标
uniform vec3 viewPos;			//观察者世界坐标

void main(){
	//计算环境光照
	float ambientStrength = 0.1;
	vec3 ambient = ambientStrength * lightColor;		//环境光照

	//计算漫反射光照
	//转为单位向量
	vec3 norm = normalize(normal);
	vec3 lightDir = normalize(lightPos - vec3(fragPos));
	float rad = max(dot(lightDir, norm), 0.0);			//光照方向与片段法向量夹角[0,PI/2]
	vec3 diffuse = rad * lightColor;							//漫反射光照

	//计算镜面光照
	vec3 viewDir = normalize(viewPos - vec3(fragPos));			//观察者方向
	vec3 reflectDir = reflect(-lightDir, norm);							//光照经法向量折射方向
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);	//反光度,这里取点乘的32次幂
	float specularStrength = 0.5;												//镜面强度
	vec3 specular = spec * specularStrength * lightColor;		//镜面光照
	
	//距离影响光照强度,暂时是我自己想的
	float enableDistance = 4.0f;	//有效光照距离
	vec3 light2fragVec = lightPos - vec3(fragPos);
	float distan = min(sqrt(light2fragVec.x * light2fragVec.x + light2fragVec.y * light2fragVec.y + light2fragVec.z * light2fragVec.z), enableDistance);
	float strength = 1 - distan / enableDistance;

	//光照远近先不影响环境光照了
	vec3 result = ambient * cubeColor + (diffuse + specular)  * cubeColor * strength;
	fragColor = vec4(result, 1.0);
}

参考:https://learnopengl-cn.github.io/02%20Lighting/02%20Basic%20Lighting/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/148793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

blender学习笔记2023.01.05

文章目录why基操why 想画条大黄鱼 想画一下渔网 网箱 写笔记预防忘记 基操 1.语言改为中文 不过后续可能改回英文去 2.顶部导航栏—编辑—偏好设置—界面—翻译—&#xff08;关掉&#xff09;新建数据 目的是预防插件导致奇奇怪怪的报错 这里左下角位置处点击 保存修改 3…

初识LCD1602及编程实现字符显示

一、LCD1602基础知识及接线方法LCD1602是一种工业字符型液晶&#xff0c;能够同时显示16x02即32字符&#xff08;16列两行&#xff09;引脚说明第 1 脚: VSS 为电源地 第 2 脚: VDD 接 5V 正电源 第 3 脚: VL 为液晶显示器对比度调整端,接正电源时对比度最弱&#xff0c;接地时…

【Neo4j构建知识图谱】:官方服务图谱大型数据集下载与可视化方法【数据集包括:食谱数据、足球、权力的游戏、美国宇航局、英国公司注册、财产所有权、政治捐款】

目录 1、服务端口免费查看知识图谱2、关于 Neo4j 示例数据集的实现3、下载离线数据集4、项目概览与实现案例还可以看到解析python源码还可以看到解析cypher源码各种数据集实现案例参考1、服务端口免费查看知识图谱 此服务器托管许多具有只读访问权限的数据集,供公众使用。 该…

2022尚硅谷SSM框架跟学(三)MyBatis基础三

2022尚硅谷SSM框架跟学 三 MyBatis基础三9.动态SQL9.1if9.2where方法一:加入恒成立的条件方法二:使用where标签9.3trim9.4choose、when、otherwise9.5foreach9.51批量添加9.52批量删除批量删除方式1批量删除方式2批量删除方式39.6SQL片段10.MyBatis的缓存10.1MyBatis的一级缓存…

JS基础(一)——认识JS及其基础语法

网页的三个组成部分 HTML&#xff1a;用于控制网页的内容CSS&#xff1a;用于控制网页的样式JavaScript&#xff1a;用于控制网页的行为 网页的行为指用户与浏览器的行为交互、浏览器与浏览器与服务器的数据交互。 ECMAScriptS&#xff08;ES&#xff09; ECMAScriptS是Java…

OpenCV入门

OpenCV入门图像金字塔高斯金字塔(cv2.pyrUp、cv.pyrDown)拉普拉斯金字塔边缘检测图像轮廓 (cv2.findContours)轮廓特征&#xff08;cv2.contourArea、cv2.arcLength&#xff09;轮廓近似(cv2.approxPolyDP)边界矩形、外接圆(cv2.boundingRect、cv2.minEnclosingCircle)模板匹配…

C库函数:time.h

time.h C 标准库 – <time.h> | 菜鸟教程 (runoob.com) 库变量 下面是头文件 time.h 中定义的变量类型&#xff1a; 序号变量 & 描述1size_t 是无符号整数类型&#xff0c;它是 sizeof 关键字的结果。2clock_t 这是一个适合存储处理器时间的类型。3time_t is 这是一…

C库函数:math.h

math.h C 标准库 – <math.h> | 菜鸟教程 (runoob.com) 16double pow(double x, double y) 返回 x 的 y 次幂。17double sqrt(double x) 返回 x 的平方根。18double ceil(double x) 返回大于或等于 x 的最小的整数值。19double fabs(double x) 返回 x 的绝对值。20doubl…

矩阵分析:QR分解

Householder变换 Householder变换是一种简洁而有意思的线性变换&#xff0c;也可称为镜面反射变换&#xff0c;Householder变换矩阵为HI−wTwHI-w^TwHI−wTw 考虑向量α\alphaα和一个单位向量w:wTw1w:w^{T}w1w:wTw1 α\alphaα在www 方向上的分量是 αw//(wTα)wwwTα\alpha _…

Python快速制作自动填写脚本:100%准确率

嗨害大家好鸭&#xff01;我是小熊猫~ 环境使用 Python 3.8Pycharm 模块使用 import requests —> 数据请求模块 pip install requestsimport parsel —> 数据解析模块 pip install parselfrom selenium import webdriver —> 自动测试模块 pip install selenium3.…

#H. Linear Approximation

Description给你一个数列A&#xff0c;希望你找出一个数字B。使得下面这个式子的值最小Abs(A1-(B1))Abs(A2-(B2))Abs(A3-(B3))..........Abs(An-(Bn))FormatInput第一行给出输入n第二行给出数列A,数字的值在[1,1e9]N<2e5Output如题Samples输入数据 152 2 3 5 5输出数据 12思…

WQS二分

本博客以一种较为少见的方式来解释WQS二分。 题目 首先&#xff0c;WQS二分用于解决什么问题&#xff1f; 我们先看一个伞兵题目&#xff1a; 有一个 nnn 个数的数组 aaa。 求在 aaa 中恰好选择 mmm 个数的情况下&#xff0c;选择的数的和的最大值。 你现在看到了这个题目&a…

Jenkins基于Blue Ocean UI构建流水线

目录 一、Blue Ocean 简介 二、Blue Ocean 安装 2.1 安装 Blue Ocean 插件 2.2 安装 Blue Ocean 版本的 Jenkins 3. 构建流水线 4. 创建流水线 5. 选择代码仓库 6. 连接Git仓库 7. 创建流水线 详细信息可以参考官网&#xff1a;Blue Ocean 入门 一、Blue Ocean 简介…

牛客竞赛每日俩题 - Day13

目录 洪泛法BFS 26进制计数字符串 洪泛法BFS 红与黑__牛客网 循环接收每组用例&#xff0c;对于每组用例进行如下操作&#xff1a; 找到‘’所在的位置&#xff0c;即起始搜索的点 使用DFS搜索地板中的每块瓷砖&#xff0c;如果是黑色&#xff0c;给计数1&#xff0c;然后像…

JavaSE学习day2_01, 数据类型

1. 数据类型 1.1 Java中数据类型的分类,重点 基本数据类型 引用数据类型 1.2 基本数据类型的四类八种 整数类型&#xff1a;byte、short、int、long 浮点类型&#xff1a;float、double 字符类型&#xff1a;char 布尔类型&#xff1a;boolean,只有两个取值,true和false…

HW13 Network Compression网络压缩

文章目录一、任务描述1、介绍知识蒸馏2、介绍架构设计二、实验1、simple baselineconfigs结构设计训练2、medium baselineconfigs3、strong baselineconfigsReLu和leakyRelu知识蒸馏一、任务描述 ●网络压缩&#xff1a;使您的模型更小而不损失性能。 ●在这个任务中&#xff…

初级算法之深度搜索

目录 ​编辑 概述&#xff1a; 个人对深搜的理解&#xff1a; 深搜模板&#xff1a; 例题&#xff1a; 题目描述 输入格式 输出格式 输入输出样例 说明/提示 代码图示&#xff1a; 概述&#xff1a; 在我们刷算法的过程中肯定会想到暴力通过&#xff0c;暴力是不需要…

分类预测 | MATLAB实现超参数优化朴素贝叶斯(Naive Bayesian)多特征分类预测

分类预测 | MATLAB实现超参数优化朴素贝叶斯(Naive Bayesian)多特征分类预测 目录 分类预测 | MATLAB实现超参数优化朴素贝叶斯(Naive Bayesian)多特征分类预测分类效果基本介绍程序设计学习小结参考资料分类效果 基本介绍 MATLAB实现超参数优化朴素贝叶斯(Naive Bayesian)多特…

行为型-策略模式

策略模式简介 策略模式&#xff08;Strategy Pattern&#xff09;属于对象的行为模式。其用意是针对一组算法&#xff0c;将每一个算法封装到具有共同接口的独立的类中&#xff0c;从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。其主要目…

【Kotlin】空安全 ⑤ ( 异常处理 | 捕获并处理异常 | 抛出自定义异常 )

文章目录一、Kotlin 中的异常处理1、捕获并处理异常2、抛出自定义异常一、Kotlin 中的异常处理 在 Kotlin 程序中 , 处理异常 的方式有 : 抛出默认异常抛出自定义异常捕获并处理异常 1、捕获并处理异常 捕获异常代码示例 : 在下面的代码中 , name 变量是可空类型变量 , 其初始…