OpenGL光照

光照在现实世界是很复杂的
游戏引擎中一般使用简单的光照模型进行模拟

如图:
在这里插入图片描述


Bling-Phong Light Model 光照模型:
1.环境光照(Ambient Lighting)
即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的
我们会使用一个环境光照常量,它永远会给物体一些颜色

2.漫反射光照(Diffuse Lighting)
模拟光源对物体的方向性影响(Directional Impact)
物体的某一部分越是正对着光源,它就会越亮

3.镜面光照(Specular Lighting)
模拟有光泽物体上面出现的亮点
镜面光照的颜色相比于物体的颜色会更倾向于光的颜色

在这里插入图片描述


在OpenGL中如何模拟光照?

大体流程:
a)创建光源和受光照物体
b)着色器中使用光照模型计算每个片段的光照颜色

先创建光源和受光照物体,都用Cube模拟
1.计算光照信息需要模型空间坐标+模型法线方向,先填充顶点信息

float vertices[] = {
	-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
};

2.创建VBO、VAO
VBO(Vertex Buffer Object)是用于存储顶点数据的对象
VAO(Vertex Array Object)是用于管理这些数据对象的对象

光源和受光照物体适用不同的VAO
因为:会频繁地对顶点数据和属性指针做出修改

unsigned int VBO;
glGenBuffers(1, &VBO);

glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// ========================= Cube

unsigned int cubeVAO;
glGenVertexArrays(1, &cubeVAO);
glBindVertexArray(cubeVAO);

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*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

// ========================= Lighting

unsigned int lightingVAO;
glGenVertexArrays(1, &lightingVAO);
glBindVertexArray(lightingVAO);

// 只需要绑定VBO不用再次设置VBO的数据,因为箱子的VBO数据中已经包含了正确的立方体顶点数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

3.定义Shader着色器代码

对于光源着色器,只需要输出光的颜色即可

//vertex shader
#version 330 core

layout (location=0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
	gl_Position = projection * view * model * vec4(aPos, 1.0f); 
}


//fragment shader
#version 330 core

out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0);
}

对于受光物体的着色器要复杂一些,需要计算根据光照模型计算输出颜色

a)顶点着色器:负责对模型空间的坐标和法线进行世界空间的转换
注意:法线转换过程需要看下缩放

#version 330 core

layout (location=0) in vec3 positionOS;
layout (location=1) in vec3 normalOS;

out vec3 positionWS;
out vec3 normalWS;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
	gl_Position = projection * view * model * vec4(positionOS, 1.0f);

	//计算世界空间坐标,乘以模型矩阵
	positionWS = vec3(model * vec4(positionOS, 1.0));

	//计算法线世界方向
	//
	//不能直接乘以模型矩阵
	//1.法向量只是一个方向向量,不能表达空间中的特定位置
	//	对于法向量,没有齐次坐标(顶点位置中的w分量),只希望对它实施缩放和旋转变换。
	//2.模型矩阵执行了不等比缩放,顶点的改变会导致法向量不再垂直于表面了
	//
	//使用一个为法向量专门定制的模型矩阵。这个矩阵称之为法线矩阵(Normal Matrix)
	//	法线矩阵:模型矩阵左上角3x3部分的逆矩阵的转置矩阵

	//计算比较耗费性能,一般都提前算出来传进去
	//mat3 normalMatrix = mat3(transpose(inverse(model)));
	//normalWS = normalMatrix * normalOS;

	//对于一般模型 没有缩放的话 直接乘就行了
	normalWS = mat3(model) * normalOS;
}

b)片断着色器,计算光照颜色

#version 330 core

in vec3 positionWS;
in vec3 normalWS;

out vec4 FragColor;

uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 cameraPos;

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

    //漫反射
    vec3 lightDir = normalize(lightPos - positionWS);
    vec3 normalDir = normalize(normalWS);
    vec3 diffuse = max(0.0, dot(normalDir, lightDir)) * lightColor;

    //高光反射
    float specularStrength = 0.5;
    vec3 viewDir = normalize(cameraPos - positionWS);
    vec3 halfDir = normalize(viewDir + lightDir);
    vec3 specular = pow(max(0.0, dot(normalDir, halfDir)), 10.0) * specularStrength * lightColor;

    vec3 lightingResult = ambient + diffuse + specular;

    FragColor = vec4(lightingResult * objectColor, 1.0);
}

4.渲染循环中传递数据,进行渲染
注意:观察矩阵和投影矩阵应该用同一个

glEnable(GL_DEPTH_TEST);

//灯光位置
glm::vec3 lightPos(0.6f, 0.4f, -1.0f);

while (!glfwWindowShouldClose(window))
{
	processInput(window);

	glClearColor(0.1f, 0.1f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	float time = (float)glfwGetTime();
	deltaTime = time - lastFrame;
	lastFrame = time;

	glm::mat4 view = camera.GetViewMatrix();

	lightPos = glm::vec3(sin(time), 0.0f, cos(time)) * 2.0f;

	glm::mat4 projection = glm::mat4(1.0f);
	projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

	glm::mat4 model = glm::mat4(1.0f);

	cubeShader.use();
	cubeShader.setM4("model", model);
	cubeShader.setM4("view", view);
	cubeShader.setM4("projection", projection);
	cubeShader.setF3("objectColor", 1.0f, 0.5f, 0.31f);
	cubeShader.setF3("lightColor", 1.0f, 1.0f, 1.0f);
	cubeShader.setF3("lightPos", lightPos);
	cubeShader.setF3("cameraPos", camera.Position);

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

	glm::mat4 lightingModel = glm::mat4(1.0f);
	lightingModel = glm::translate(lightingModel, lightPos);
	lightingModel = glm::scale(lightingModel, glm::vec3(0.3f));

	lightingShader.use();
	lightingShader.setM4("model", lightingModel);
	lightingShader.setM4("view", view);
	lightingShader.setM4("projection", projection);

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

	glfwSwapBuffers(window);
	glfwPollEvents();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值