OpenGL(11)之材质

695 阅读5分钟

title: OpenGL(11)之材质

date: 2020-07-19 9:00

category: 图形学

tags: opengl

OpenGL(11)之材质

项目代码可参见 3.2.materials_exercise1

1.概述

现实生活中,每个物体对光产生不同的反应(即物体对光的吸收与反射程度不一致导致的差异性),此中涉及到了物体的反射率、介质、光的入射角等知识相关(有兴趣的可自行查阅相关知识),其中最重要当属物体本身了,不同材料的表面具有不同的反射率。 例如:钢材通常比陶瓷材质的物体显得更加闪闪发亮。如果想在OpenGL中模拟多种类型的物体, 必须为每个物体分别定义一个材质属性。

通过上两章的学习,我们知道指定一个物体的颜色和光的颜色,结合环境光和镜面强度分量,可以用来定义物体的视觉输出。

通过环境光照、漫反射光照、镜面光照以及反光度来定义一个材质颜色,即定义了材质属性: 我们使用一个结构体的形式装载这些类型。

#version 330 core
struct Material{
	vec3 ambient;//环境光照分量
	vec3 diffuse;//漫反射光照分量
	vec3 specular;//镜面光照分量
	float shininess;//反光度:影响镜面高光的散射/半径
};
uniform Material material;

上述代码片段发生在片段着色器中,我们通过创建一个结构体来存储物体的材质属性, 当然我们可以将其存储为独立的uniform值,用一个结构体来存储(符合面向对象的思想),条理清晰。

通过上述四个元素来定义一个物体的材质,通过其能够模拟很多现实世界中的材质,以下就是几种现实世界的材质对立方体的影响。

可以看出,通过正确的指定一个物体的材质属性,对于这个物体的感知也会不同,效果显而易见,为一个物体赋予一款合适的材质是非常困难的,由于不合适的材质可能会毁了物体的视觉质量。接下来看看如何为一个物体设置材质吧。

2.设置材质

在上述过程中,已经为片段着色器创建了一个材质结构体的uniform,因此我们可以从uniform变量material中访问这些属性,从而达到修改光照的计算以便顺应新的材质属性。

void main(){

	//环境光
	vec3 ambient = lightColor * material.ambient;

	//漫反射
	vec3 normal = normalize(Normal); //标准化的法向量
	vec3 lightDirection = normalize(lightPos - FragPos); //光的方向向量
	float diff = max(dot(normal,lightDirection),0.0);//漫反射影响
	vec3 diffuse = lightColor * (diff * material.diffuse);//漫反射分量

	//镜面光
	vec3 viewDirection = normalize(viewPos - FragPos);//视线方向向量
	vec3 reflectDirection = reflect(-lightDirection,normal);//反射向量
	float spec = pow(max(dot(viewDirection,reflectDirection),0.0),material.shininess); //反光度
	vec3 specular = lightColor * (spec * material.specular);//镜面光照分量

	vec3 result = ambient + diffuse + specular;

	FragColor = vec4(result,1.0); //最终组合光照的结果
}

上述设置完之后,我们可以在程序中设置适当的uniform,对物体设置材质。但是切记,我们需要对每个单独的uniform进行设置,并且要带上结构体名的前缀;

lightingShader.setVec3("material.ambient",1.0f,0.5f,0.31f);
lightingShader.setVec3("material.diffuse",1.0f,0.4f,0.42f);
lightingShader.setVec3("material.specular",0.5f,0.5f,0.5f);
lightingShader.setFloat("material.shininess",32.0f);

将环境光和漫反射分量设置为想要物体拥有的颜色,将镜面分量设置为一个中等亮度的颜色,反光度保持为32。

3.光的属性

上述物体显得太亮了,原因是环境光、漫反射和镜面光这三个颜色对任何一个光源都会全力反射。光源对环境光、漫反射和镜面光分量也具有着不同的强度,可以通过使用一个强度值来改变环境光和镜面光强度的方式解决这个问题,因此可以为每个光照分量指定一个强度向量,例如这样

vec3 ambient = vec3(1.0) * material.ambient;
vec3 diffuse = vec3(1.0) * (diff * material.diffuse);
vec3 specular = vec3(1.0) * (spec * material.specular);

如上述代码片段,我们知道物体的每个材质属性对每一个光照分量都返回了最大的强度。

实际上,环境光分量完全影响了物体的颜色,为此,应该减小环境光对物体的影响(即减小强度),例如这样:

vec3 ambient = vec3(0.1) * material.ambient;

同理用同样的方式改变其他两个分量对物体的影响,这里通过定义一个结构体的形式来统一管理各个分量光照强度对物体的影响;

struct Light{
	vec3 position;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
};
uniform Light light;

一个光源对其环境光(ambient)、漫反射(diffuse)和镜面光(specular)光照有不同的强度,环境光通常会设置为一个比较低的强度,光源的漫反射分量通常设置为光所具有的颜色,通常为一个比较明亮的白色,镜面光分量通常会保持为vec3(1.0),以最大强度发光。根据上述定义的结构体Light,此时就需要更新片段着色器了。

vec3 ambient = light.ambient * material.ambient;

vec3 diffuse = light.diffuse * material.diffuse;

vec3 specular = light.specular * material.specular;

紧接着在程序中设置光照强度:

lightingShader.setVec3("light.ambient",0.2f,0.2f,0.2f);
lightingShader.setVec3("light.diffuse",0.5f,0.5f,0.5f); //调暗,搭配场景
lightingShader.setVec3("light.specular",1.0f,1.0f,1.0f);

经过调整光对物体材质影响,此次可以得到较好的视觉效果,并且对光照和物体材质有效的进行掌控。

4.光源颜色的差异性

现阶段,对光源的设置局限于从白到灰到黑范围内的颜色,这样做的只会改变物体各个分量的强度,而不是改变光源真正的颜色。

我们通过修改光源的颜色,来创造一些很有趣的效果。通过改变光源颜色,能够极大地影响物体的最终输出颜色,从而达到一种显著的视觉效果。

可以利用sin和glfwGetTime函数改变光源的环境光和漫反射颜色,从而很容易地让光源的颜色随着时间变化;

glm::vec3 lightColor;
lightColor.x = sin(glfwGetTime()*2.0f);
lightColor.y = sin(glfwGetTime()*0.5f);
lightColor.z = sin(glfwGetTime()*1.3f);

glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f); // 调暗,场景需要

glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); //降低环境光的影响
lightingShader.setVec3("light.ambient",ambientColor);
lightingShader.setVec3("light.diffuse",diffuseColor);


参考

LearnOpenGL

材质