OpenGL原理与实践——核心模式(五):颜色、基础光照、Phong模型、材质与光

news2025/7/15 1:26:09

目录

颜色相关理论

什么是颜色

如何计算颜色?

简单实现

Phong光照模型——局部光照模型

环境光

​编辑

漫反射

镜面反射

材质与光

材质与纹理的关系

材质在shader的体现

材质属性与光属性

光在shader的体现

整体源码实现及渲染结果

关键代码

shader——vertexShader.glsl(读取物体数据)

shader——vsunShader.glsl(读取光源数据)

shader——fragmentShader.glsl(定义材质、光结构体数据结构,读取数据并计算像素颜色)

shader——fsunShader.glsl( 定义光源颜色)

render——传入shader数据并渲染

渲染结果


颜色相关理论

什么是颜色

说白了就是光反射进人眼的那部分。

如何计算颜色?

以这个图为例,设自然光照为RGB分量为(1,1,1),一个物体的颜色RGB为(1,0.5,0.31),由之前所说的颜色理论,物体的颜色即为反射光照所占光源RGB百分比,所以直接相乘即可得到最终颜色。

再比如,如果光源本身就没有红色(RGB中的R分量为0),那么物体自然也不会反射红光,即R值。所以最终颜色的结果的R也是0。

简单实现

我们需要一个光源和一个物体,那么自然需要构建两组VAO,并进行VBO的绑定;同时设定光源的位置和光源颜色:

//main.cpp
VAO_cube = createModel();
VAO_sun = createModel();
light_pos = glm::vec3(3.0f, 0.0f, -1.0f);
light_color = glm::vec3(1.0f, 1.0f, 1.0f);
uint createModel()
{
	uint _VAO = 0;
	uint _VBO = 0;

	float vertices[] = {
		-0.5f, -0.5f, -0.5f,  0.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, 1.0f,           0.0f,  0.0f, -1.0f,
		 0.5f,  0.5f, -0.5f,  1.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, 0.0f,           0.0f,  0.0f, -1.0f,

		-0.5f, -0.5f,  0.5f,  0.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, 1.0f,           0.0f,  0.0f,  1.0f,
		 0.5f,  0.5f,  0.5f,  1.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, 0.0f,           0.0f,  0.0f,  1.0f,

		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,           -1.0f,  0.0f,  0.0f,
		-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,           -1.0f,  0.0f,  0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,           -1.0f,  0.0f,  0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.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,  1.0f, 0.0f,           -1.0f,  0.0f,  0.0f,

		 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,           1.0f,  0.0f,  0.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,           1.0f,  0.0f,  0.0f,
		 0.5f, -0.5f, -0.5f,  0.0f, 1.0f,           1.0f,  0.0f,  0.0f,
		 0.5f, -0.5f, -0.5f,  0.0f, 1.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,  1.0f, 0.0f,           1.0f,  0.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,  1.0f, 1.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,  0.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.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,  1.0f,  0.0f,
		 0.5f,  0.5f, -0.5f,  1.0f, 1.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,  0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,           0.0f,  1.0f,  0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,           0.0f,  1.0f,  0.0f,
	};


	glGenVertexArrays(1, &_VAO);
	glBindVertexArray(_VAO);

	glGenBuffers(1, &_VBO);
	glBindBuffer(GL_ARRAY_BUFFER, _VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(sizeof(float) * 3));
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(sizeof(float) * 5));

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
	glEnableVertexAttribArray(2);

	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	return _VAO;
}

Phong光照模型——局部光照模型

 

Phong光照模型是环境光、漫反射和镜面反射的结合。

环境光

一般是一个常数,物体一直显示的颜色。来代表周围环境的光照。

在shader中的实现为:

可以看到这里直接设置为了一个常数系数,然后直接乘以光源光、乘以物体颜色即可完成运算。

漫反射

入射的光线可以分为两个向量,只有垂直于平面的分量对光照亮度有贡献。说白了就是物体实际反射出来的光。

shader:

 

也就是说,我们只需要计算法向量与光线向量的cos夹角(单位向量即点乘),并将这个cos赋为系数,乘以光源的颜色,即可得出漫反射的光照。

值得注意的是,Normal 向量的计算是一个齐次坐标阵,并且最后一个元素为0,代表是向量。此外,对于缩放变换,有可能导致法向量Normal进行错误的变换,这里需要做特殊的处理:

镜面反射

  • 考虑入射光和反射光位置的反射方式
  • 光强考虑观察者位置,视线与反射光夹角决定了光强

shader中的具体计算方式:

光线方向需要取反,因为实际入射光是从光源射到某个点上的。之前我们定义的是从某点射到光源上的。

材质与光

材质:物体对于某一种光的反射强度,在Phong模型下,就是不同物体对环境光、漫反射、镜面反射等不同光的反射强度定义。

比如:粘土罐子对于镜面反射几乎没有反应,但是对于漫反射会有很强的反应。

从RGB的角度来讲,可以理解为对RGB的做进一步的“百分比”。

材质与纹理的关系

  • 纹理:可理解为普通的贴图,即物体本身最基础的颜色
  • 材质:给物体表面做的进一步的光源反射属性。比如,具有金属质感(材质)木头(纹理)、具有木头质感(材质)金属(纹理)

材质在shader的体现

材质属性与光属性

  • 材质属性:我们可以根据自己的喜好来决定如何反射光
  • 光属性:我们可以根据自己的喜好来决定如何发射光

光在shader的体现

 

整体源码实现及渲染结果

上面说了这么多,需要落实到代码中去

关键代码

shader——vertexShader.glsl(读取物体数据)

//vertexShader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aUV;
layout (location = 2) in vec3 aNormal;

out vec2 outUV;
out vec3 outFragPos;
out vec3 outNormal;


uniform mat4 _modelMatrix;
uniform mat4 _viewMatrix;
uniform mat4 _projMatrix;

void main()
{
   gl_Position = _projMatrix * _viewMatrix * _modelMatrix * vec4(aPos.x, aPos.y, aPos.z, 1.0);
   outUV = aUV;
   outFragPos = vec3( _modelMatrix * vec4(aPos.x, aPos.y, aPos.z, 1.0));
   outNormal = mat3(transpose(inverse(_modelMatrix))) * aNormal;
};

shader——vsunShader.glsl(读取光源数据)

//vsunShader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aUV;

out vec2 outUV;

uniform mat4 _modelMatrix;
uniform mat4 _viewMatrix;
uniform mat4 _projMatrix;


void main()
{
   gl_Position = _projMatrix * _viewMatrix * _modelMatrix * vec4(aPos.x, aPos.y, aPos.z, 1.0);
   outUV = aUV;
};

shader——fragmentShader.glsl(定义材质、光结构体数据结构,读取数据并计算像素颜色)

#version 330 core
out vec4 FragColor;

in vec2 outUV;
in vec3 outFragPos;
in vec3 outNormal;

struct Material
{
    vec3 m_ambient;
    vec3 m_diffuse;
    vec3 m_specular;

    float m_shiness;
};

uniform Material myMaterial;

struct Light
{
    vec3 m_pos;
    vec3 m_ambient;
    vec3 m_diffuse;
    vec3 m_specular;
};

uniform Light myLight;


uniform sampler2D  ourTexture;
uniform vec3 view_pos;

void main()
{
//环境光
    vec3 _ambient = myLight.m_ambient * myMaterial.m_ambient;

//漫反射
    vec3 _normal = normalize(outNormal);
    vec3 _lightDir = normalize(myLight.m_pos - outFragPos);
    float _diff = max(dot(_normal , _lightDir) , 0.0f);
    vec3 _diffuse = myLight.m_diffuse * _diff * myMaterial.m_diffuse;

//镜面反射
    float _specular_strength = 0.5;
    vec3 _viewDir = normalize(view_pos - outFragPos);
    vec3 _reflectDir = reflect(-_lightDir , outNormal);

    float _spec = pow(max(dot(_viewDir , _reflectDir) , 0.0f) , myMaterial.m_shiness);

    vec3 _sepcular = myLight.m_specular * _spec * myMaterial.m_specular;


    vec3 result = _ambient  + _diffuse + _sepcular;
    FragColor = texture(ourTexture , outUV) * vec4(result ,1.0f);
};

shader——fsunShader.glsl( 定义光源颜色)

#version 330 core
out vec4 FragColor;

in vec2 outUV;

uniform sampler2D  ourTexture;

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

 render——传入shader数据并渲染

void render()
{
	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);


	_camera.update();
	_projMatrix = glm::perspective(glm::radians(45.0f), (float)_width / (float)_height, 0.1f, 100.0f);
	glm::mat4 _modelMatrix(1.0f);
	_modelMatrix = glm::translate(_modelMatrix, glm::vec3(0.0f, 0.0f, -3.0f));
	glBindTexture(GL_TEXTURE_2D, _texture);

	//物体shader
	_shader_cube.start();
		_shader_cube.setVec3("view_pos", _camera.getPosition());

		//传入光照属性
		light_color = glm::vec3((float)glfwGetTime() * 0.4f, (float)glfwGetTime() * 0.5f, (float)glfwGetTime() * 0.7f);
		_shader_cube.setVec3("myLight.m_ambient", light_color * glm::vec3(0.1f));
		_shader_cube.setVec3("myLight.m_diffuse", light_color * glm::vec3(0.7f));
		_shader_cube.setVec3("myLight.m_specular", light_color * glm::vec3(0.5f));
		_shader_cube.setVec3("myLight.m_pos", light_pos);

		//传入物体材质属性
		_shader_cube.setVec3("myMaterial.m_ambient", glm::vec3(0.1f));
		_shader_cube.setVec3("myMaterial.m_diffuse", glm::vec3(0.7f));
		_shader_cube.setVec3("myMaterial.m_specular", glm::vec3(0.8f));
		_shader_cube.setFloat("myMaterial.m_shiness", 32);

		_shader_cube.setMatrix("_modelMatrix", _modelMatrix);
		_shader_cube.setMatrix("_viewMatrix", _camera.getMatrix());
		_shader_cube.setMatrix("_projMatrix", _projMatrix);
		glBindVertexArray(VAO_cube);
		glDrawArrays(GL_TRIANGLES, 0, 36);
	_shader_cube.end();

	//光源shader
	_shader_sun.start();
		_shader_sun.setMatrix("_modelMatrix", _modelMatrix);
		_shader_sun.setMatrix("_viewMatrix", _camera.getMatrix());
		_shader_sun.setMatrix("_projMatrix", _projMatrix);

		_modelMatrix = glm::mat4(1.0f);
		_modelMatrix = glm::translate(_modelMatrix, light_pos);
		_shader_sun.setMatrix("_modelMatrix", _modelMatrix);
		glBindVertexArray(VAO_sun);
		glDrawArrays(GL_TRIANGLES, 0, 36);
	_shader_cube.end();
}

渲染结果

 

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

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

相关文章

软件测试员如何在恶劣的内卷环境下脱颖而出?

内卷,是现在热度非常高的一个词汇,随着热度不断攀升,隐隐到了“万物皆可卷”的程度。 我一个很要好的朋友,现在就读大三,像很多大学生一样面临着能否顺利毕业的压力和考证的焦虑,看着寝室四个人每天都在玩&…

Vue--》混合文件使用以及ref的引用讲解

目录 mixin混合 前言 ref的引用DOM ref引用组件 mixin混合 在日常开发中,当我们开发的各种组件可能会有相同的内容,我们可以将相同的内容在各个相对应的组件内删除,然后放在同一个配置里。所谓混合:两个或多个组件共享一个配…

数据挖掘,在商业智能BI领域的运用

数据挖掘在商业领域,特别是在零售业的运用是比较成功的。由于各业务系统的普遍使用,再加上商业智能BI的可视化分析,企业可以收集到大量关于购买情况的数据,并且数据量在不断激增。利用数据挖掘技术可以为经营管理人员提供正确的决…

Java 集合

目录 一、概念 二、接口 2.1、 集合接口 2.2、 Set 接口 2.2.1 zise方法 2.2.2 isEmpty 方法 2.2.3 contains 方法 2.2.4 Iterator 方法 2.2.5 toArray 方法 2.2.6 add 方法 2.2.7 remove 方法 2.2.8 containsAll 方法 2.2.9 containsAll 方法 2.2.10 ret…

数据仓库基础

文章目录1 数据仓库1.1 数据仓库为何而来1.2 数据仓库主要特征1.2.1 面向主题1.2.2 集成性1.2.3 非易失性1.2.4 时变性1.3 数据仓库、数据库、数据集市1.3.1 OLTP1.3.2 OLAP1.3.3 OLTP和OLAP的对比1.3.4 数据库和数据仓库的区别1.3.5 数据仓库和数据集市的区别1.4 数据仓库分层…

Spring Boot Admin2 自定义异常监控

其他相关文章: Spring Boot Admin 参考指南SpringBoot Admin服务离线、不显示健康信息的问题Spring Boot Admin2 EnableAdminServer的加载Spring Boot Admin2 AdminServerAutoConfiguration详解Spring Boot Admin2 实例状态监控详解Spring Boot Admin2 自定义JVM监控…

Java项目:JSP旅游产品销售管理系统

作者主页:源码空间站2022 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台,分为管理员与普通用户两种角色,管理员登录后台,普通用户登录前台; 管理员角色…

Docker-CentOS开启防火墙firewalled映射Docker端口

开启docker的Tomcat容器后,启动 docker run -d -p 8080:8080 tomcat 访问不了Tomcat 查看防火墙所有开放的端口 firewall-cmd --zonepublic --list-ports 一、需要防火墙开启8080 端口 1、通过systemctl status firewalld查看firewalld状态,发现当前…

03.OpenWrt-系统固件烧录

03.OpenWrt-系统固件烧录 3.1 tft软件烧录 tftp是运行在windows的软件,是将windows主机作为服务端,OpenWrt开发板作为客户端,通过tftp协议将数据发送到开发板进行数据升级. 3.1.1 tftp升级的连接方式 tftp烧录有两种连接方式: windows主机通过有线或者无线的方式连接到路由…

IDEA 代码提交前流程及提交日志模板化

前言 在开发大型项目时,通常都是由团队来进行开发。此时,每个人有每个人的代码编写风格和提交习惯,如果放任自由发挥,那么代码质量和代码提交日志就难免风格各异,导致项目代码质量难以保持统一。针对这一问题&#xf…

Flutter高仿微信-第44篇-群聊

Flutter高仿微信系列共59篇,从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图: 实现代码: group_chat_main.dart /*** Author : wangning* Email …

Unity VR 开发教程: Oculus 一体机开发 (一) 环境配置(基于 Oculus Integration v46)

文章目录📕教程说明📕安装 Unity 时需要添加的模块📕设置 Unity 的 Build Settings📕导入 Oculus Integration📕设置 Project Settings⭐通用设置⭐Rendering 设置⭐Identification 设置⭐Configuration 设置⭐XR Plug…

信号类型(雷达)——雷达波形认识(一)

系列文章目录 《信号类型(雷达通信)》 文章目录 前言 简述 总结 前言 本文将结合个人研究经验,从雷达波形简单谈谈我对雷达的认识。之后将对常见的雷达波形进行简单分析。 简述 雷达的波形决定了信号处理的手段以及对应的雷达功能&#x…

关于WEB端实现电子海图研究二GeoServer

记笔记,免忘记! 接前面思路篇。本文章主要是讲,利用GeoServer对shp文件进行数据样式调整 ,数据拼接,shp文件发布,矢量切片。 Geoserver官网:GeoServer 可以使用最新版本,我使用的…

边缘计算那些事儿--网络切片技术(1)

0 背景 边缘计算可以支持就近的计算卸载,让数据在靠近数据源的设备上处理。对于边缘计算低时延的应用场景来说,网络性能的好坏,直接影响着卸载算法的整体耗时,决定着整个算法模型的可行性。因此如果想实现低时延卸载算法&#xff…

Go语言入门【7】指针

指针 在go语言中,每一个变量在运行时都会拥有一个地址,这个地址代表的就是变量在内存中的位置,而指针就是指向这个地址的变量。使用go语言中的指针很简单,只需要记住两个关键字符,&(取地址符&#xff…

[附源码]java毕业设计在线二手车交易信息管理系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

【App自动化测试】(九)移动端复杂测试环境模拟——来电、短信、网络切换

目录1. 发送短信2. GSM电话3. 设置模拟信号强弱4. 设置网速5. 设置网络连接类型前言: 本文为在霍格沃兹测试开发学社中学习到的一些技术写出来分享给大家,希望有志同道合的小伙伴可以一起交流技术,一起进步~ 😘 当我们使用模拟器来…

C#重启 --- 类和对象

​​​​​​ 1.类是抽象的,对象是具体的 2.类中有数据成员和方法成员(数据成员是名词性的,方法成员是动词性的) 1.类的关键字是class (变量:首字母小写,方法:首字母大写&#x…

【毕业设计】机器学习的员工离职模型研究-python

目录 前言 课题背景和意义 实现技术思路 变量分析 数据导入 构建机器学习模型 1. 1 复制数据删除不需要的变量 1.2 列变量属性分类 实现效果图样例 前言 📅大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设…