计算机图形学:实验三 光照与阴影
一、程序功能设计
设置了一个3D渲染场景,支持通过键盘和鼠标控制交互,能够动态调整光源位置、物体材质参数等,具有光照、阴影和材质效果的场景渲染。
OpenGL物体渲染和设置
- 创建3D物体:代码中通过 openGLObject 结构体表示一个3D物体,包含了顶点数据、着色器程序以及与物体的变换矩阵相关的信息。每个物体可以有多个顶点(如位置、颜色、法向量等),以及对应的材质、光源、阴影等属性。
- 绑定物体数据到OpenGL:bindObjectAndData 函数用于将物体的顶点、颜色、法线等数据传输给OpenGL。该函数使用 glGenVertexArrays 和 glGenBuffers 创建 VAO 和 VBO,并设置着色器的顶点属性(如位置、颜色、法向量等)。它还配置了物体的变换矩阵,使得物体可以在3D空间中进行旋转、缩放等操作。
光照和材质管理
- 光源和材质传递到着色器:bindLightAndMaterial 函数将物体的光源信息(如环境光、漫反射光、镜面反射光)和材质信息(如物体的环境光、漫反射、镜面反射强度)传递到着色器中。通过设置光源的位置、颜色等,影响物体的渲染效果。每个物体在渲染时都可以拥有不同的材质参数,例如镜面反射的高光系数。
- 计算相机位置:该函数还将相机的位置传递给着色器,影响光照计算中的视角部分,从而可以实现如镜面反射等效果。
渲染和变换矩阵处理
- 视图矩阵和投影矩阵:在 display 函数中,程序首先计算视图矩阵和投影矩阵,以确定摄像机的位置和视野范围。这些矩阵帮助实现3D物体的透视效果,并确定物体在屏幕上的位置。
- 变换操作:物体的变换(如旋转、缩放、平移)通过变换矩阵传递给着色器。着色器程序会使用这些变换矩阵来渲染物体的位置和方向。
光照和阴影效果
- 动态调整光源:通过键盘或鼠标操作,用户可以动态调整光源的位置,从而影响光照效果。这意味着场景中的物体会随光源的移动而产生不同的阴影和光照效果。
- 阴影计算:在 display 函数中,还计算并渲染了光源的投影效果。通过光源与物体的关系,程序生成阴影效果,创建更加真实的视觉效果。
交互功能
- 键盘交互:通过检测不同的按键和操作(按下、按住等),可以控制物体的颜色、材质、位置等。按键包括功能键(如 ESC、H、Q)和数字键(如 1、2、3 等),用于调整 ambient、diffuse 和 specular 的分量(RGB)以及物体的平移(通过 LEFT、RIGHT、UP、DOWN 控制)。
- 鼠标交互:鼠标左键按下时,可以获取当前鼠标的坐标,并根据这些坐标将光源的位置调整到与鼠标位置对应的屏幕坐标范围内。x 和 y 坐标转换为 -1 到 1 之间的值,并根据这个值设置光源位置。
二、程序代码实现
绘制场景
使用一个灰色平面来对物体的黑色阴影进行呈现。
添加平面呈现阴影
- 添加一个TriMesh*类型的plane:plane用作计算和展示物体阴影的投影平面。光源投射到该平面上的阴影通过投影矩阵计算,并渲染在平面上,同时plane通常位于场景的底部(如y=0平面)以便显示物体的阴影效果。
- 添加一个openGLObject类型的plane_object:plane_object 可以应用平移、旋转、缩放等变换操作来调整平面在场景中的位置和方向。它与 plane紧密配合,用于正确呈现物体的阴影效果。
在init中生成平面
- 生成平面并设置其变换:调用 plane->generateSquare() 方法生成一个平面(Square)。配置平面的位置(setTranslation)、旋转(setRotation)和缩放(setScale)。设置平面材质的环境光、漫反射、镜面反射和高光系数(为一个灰色平面)。
- 将平面的顶点数据传递给着色器:使用 bindObjectAndData() 将 plane 对象的数据绑定到 plane_object,并传递给着色器程序。
在display中渲染平面
- 使用 glBindVertexArray() 和 glUseProgram() 绑定 plane_object 对象的顶点数组对象(VAO)和着色器程序。
- 调用 bindLightAndMaterial() 将光源和物体材质的数据传递给着色器。
- 设置平面的变换矩阵 modelMatrix 并传递给着色器。
- 设置 isShadow 参数为 0,表示平面正常渲染。
- 最后绘制平面。
设置相机
设置相机并添加交互,实现从不同位置/角度、以正交或透视投影方式观察场景。
视图矩阵
getViewMatrix 方法通过调用 lookAt() 方法返回当前相机的视图矩阵。
lookAt() 方法使用了glm::lookAt()来根据相机的位置(eye)、目标点(at)和上方向(up)计算视图矩阵。通过 eye、at 和 up 向量来指定相机的方向和位置。
投影矩阵
getProjectionMatrix 方法根据isOrtho标志来选择不同的投影类型:
- 如果是正交投影(isOrtho为真),则调用 ortho() 方法。
- 如果是透视投影(isOrtho为假),则调用 perspective() 方法。
正交投影和透视投影的实现
- ortho:实现正交投影矩阵。正交投影不考虑远近距离,因此物体的大小在视野中保持不变。
- perspective:实现透视投影矩阵。透视投影模拟了相机视野中的深度感知,远离视点的物体看起来更小。
更新相机位置和视角
updateCamera:更新相机的位置和视角。通过rotateAngle和upAngle来调整相机的旋转角度,radius来控制相机与目标点(at)的距离。
- rotateAngle 控制相机绕目标点的水平旋转(水平视角)。
- upAngle 控制相机的俯仰角度(垂直视角)。
- radius 控制相机与目标点的距离。
键盘事件处理
keyboard:根据键盘输入来改变相机的状态(例如旋转、缩放、切换投影方式等)。
- U键:控制相机绕目标点的旋转(顺时针/逆时针)。
- I键:控制相机的俯仰角度(上下调整)。
- O键:控制相机与目标点的距离(放大/缩小)。
- P键:切换投影方式(透视/正交)。
- 空格键:将相机参数重置为默认值。
添加光照和材质效果
在init中设置光源参数
创建并配置光源 light 的位置和光照参数(环境光、漫反射、镜面反射),设置光源在三维空间中的位置 (0.0, 0.0, 2.0)。
在init中设置模型
- 设置物体的变换:mesh(物体)对象的平移、旋转和缩放变换:平移设置为 (0.0, 0.0, 1.0),即物体在z轴上偏移。旋转和缩放设置为默认值 (0.0, 0.0, 0.0) 和 (1.0, 1.0, 1.0)。在这里需要注意的是,如果物体与平面太贴近无法正确观察阴影时,可以调整setTranslation的第三个参数来改变物体和平面的垂直距离,更好地观察阴影。
- 设置物体的材质:配置 mesh 对象的环境光、漫反射、镜面反射和高光系数。
- 绑定物体的顶点数据到着色器:使用 bindObjectAndData() 函数将 mesh 对象的数据传递给着色器程序。
在display中渲染模型
- 使用 glBindVertexArray() 和 glUseProgram() 绑定 mesh_object 对象的顶点数组对象(VAO)和着色器程序。
- 调用 bindLightAndMaterial() 将光源和物体材质的数据传递给着色器。
- 创建物体的变换矩阵 modelMatrix 并将其传递给着色器,使用 glUniformMatrix4fv() 设置物体的变换矩阵、视图矩阵和投影矩阵。
- 使用 glUniform1i() 设置着色器的 isShadow 参数为 0,表示物体不是阴影。
- 绘制物体的三角形顶点。
添加阴影效果
光源的投影矩阵计算
getShadowProjectionMatrix()计算一个阴影投影矩阵,用于将光源的位置投影到一个平面上,通常是在 Y=0 平面上。
- 获取光源的位置:首先,通过 getModelMatrix() 获取光源的模型矩阵。光源的位置存储在 translation 中。使用 modelMatrix * glm::vec4(this->translation, 1.0) 计算光源在世界坐标系中的位置,结果存储在 light_position 中。
- 提取光源的坐标: lx, ly, lz 分别提取光源的世界坐标(light_position[0] 是 x 坐标,light_position[1] 是 y 坐标,light_position[2] 是 z 坐标)。
- 构建阴影投影矩阵:构建一个矩阵用于将物体投影到Y=0平面上。具体来说,该矩阵通过把物体的 z 坐标值映射到 Y=0 平面,将物体位置与光源方向的关系映射出来。
在display中添加阴影
- 计算阴影投影矩阵:将光源的投影矩阵getShadowProjectionMatrix()与物体的 modelMatrix 相乘,得到阴影的变换矩阵。
- 更新变换矩阵并将 shadowLocation 设置为 1,表示这是阴影绘制。
- 绘制阴影的三角形(GL_TRIANGLE_FAN)。
交互控制光源位置并更新阴影
mouse_button_callback 函数是处理鼠标按键事件的回调函数,主要功能是当鼠标左键按下时,获取鼠标的位置并将其映射到标准化设备坐标系,然后更新光源的位置。
- glfwGetCursorPos 用于获取当前鼠标在窗口中的像素位置。
- half_winx 和 half_winy 是窗口宽高的一半,用于将屏幕坐标转换成标准化坐标(NDC)。标准化坐标系的范围是 [-1, 1],其中 (0, 0) 是屏幕中心。
- lx 和 ly 将鼠标的屏幕坐标(像素)转换为标准化坐标,lx 和 ly 的值会在 [-1, 1] 范围内变化。这里需要注意的是ly 需要做反转处理(HEIGHT - y),因为在屏幕坐标系中,y 坐标的原点通常位于左上角,而标准化坐标系中的 y 原点通常在中心,且 y 坐标值是递增的。
- 光源的位置通过 light->getTranslation() 获取,并通过 light->setTranslation() 设置新的位置。这里只更新光源的 x 和 y 坐标,z 坐标保持不变。
三、程序运行结果
运行程序初始界面
调整光源位置
点击鼠标左键,改变光源位置,观察阴影的形成。效果图如下图所示。
观察不同物体形成的阴影
分别点击A、W、S、Q键,改变物体模型,观察不同物体的阴影形成。
调整物体的材质
点击0-9改变物体的材质:
- (shift) + 1/2/3:改变环境光的参数。
- (shift) + 4/5/6:改变漫反射的参数。
- (shift) + 7/8/9:改变镜面反射的参数。
- (shift) + 0:改变光泽度参数。
相关文章:

计算机图形学:实验三 光照与阴影
一、程序功能设计 设置了一个3D渲染场景,支持通过键盘和鼠标控制交互,能够动态调整光源位置、物体材质参数等,具有光照、阴影和材质效果的场景渲染。 OpenGL物体渲染和设置 创建3D物体:代码中通过 openGLObject 结构体表示一个…...
「 机器人 」扑翼飞行器混合控制策略缺点浅谈
前言 将基于模型的控制与强化学习策略融合在扑翼飞行器中,虽然能够兼顾系统稳定性与极限机动能力,但也面临了更高的系统复杂性、对硬件算力与可靠性的额外要求,以及难以回避的能量效率等方面挑战。以下从四个方面进行归纳与分析。 1. 系统复杂性增加 1.1 两种控制方法的并存…...

蓝桥杯算法日常|c\c++常用竞赛函数总结备用
一、字符处理相关函数 大小写判断函数 islower和isupper:是C标准库中的字符分类函数,用于检查一个字符是否为小写字母或大写字母,需包含头文件cctype.h(也可用万能头文件包含)。返回布尔类型值。例如: #…...

每日十题八股-2025年1月24日
1.面试官:Kafka 百万消息积压如何处理? 2.面试官:最多一次、至少一次和正好一次有什么区别? 3.面试官:你项目是怎么存密码的? 4.面试官:如何设计一个分布式ID? 5.面试官:单点登录是怎么工作的…...
tomcat的accept-count、max-connections、max-threads三个参数的含义
tomcat的accept-count、max-connections、max-threads三个参数的含义 tomcat的accept-count、max-connections、max-threads三个参数的含义 max-connections:最大连接数 最大连接数是指,同一时刻,能够连接的最大请求数 需要注意的是&#x…...
【无标题】mysql python 连接
coding:utf8 import os import pymysql import yaml from common.log import logger class Mysql: # 处理.sql备份文件为SQL语句 def __read_sql_file(self,file_path): # 打开SQL文件到f sql_list = [] with open(file_path, ‘r’, encoding=‘utf8’) as f: # 逐行读取和…...
linux naive代理设置
naive linux客户端 Release v132.0.6834.79-2 klzgrad/naiveproxy GitHub Client setup Run ./naive with the following config.json to get a SOCKS5 proxy at local port 1080. {"listen": "socks://127.0.0.1:1080","proxy": "htt…...

[STM32 - 野火] - - - 固件库学习笔记 - - -十一.电源管理系统
一、电源管理系统简介 电源管理系统是STM32硬件设计和系统运行的基础,它不仅为芯片本身提供稳定的电源,还通过多种电源管理功能优化功耗、延长电池寿命,并确保系统的可靠性和稳定性。 二、电源监控器 作用:保证STM32芯片工作在…...

DBO优化最近邻分类预测matlab
蜣螂优化算法(Dung Beetle Optimizer,简称 DBO)作为一种新兴的群智能优化算法,于 2022 年末被提出,其灵感主要来源于蜣螂的滚球、跳舞、觅食、偷窃以及繁殖等行为。 本次使用的数据为 Excel 格式的分类数据集。该数据…...

【深入理解FFMPEG】命令行阅读笔记
这里写自定义目录标题 第三章 FFmpeg工具使用基础3.1 ffmpeg常用命令3.1.13.1.3 转码流程 3.2 ffprobe 常用命令3.2.1 ffprobe常用参数3.2.2 ffprobe 使用示例 3.3 ffplay常用命令3.3.1 ffplay常用参数3.3.2 ffplay高级参数3.3.4 ffplay快捷键 第4章 封装与解封装4.1 视频文件转…...

图形化数据报文转换映射工具
目录 概要整体架构流程技术名词解释技术细节小结 概要 在当今数字化时代,数据的处理和分析是企业、科研机构以及各类组织日常运营的核心环节。数据来源广泛,格式多样,常见的数据格式包括XML(可扩展标记语言)和JSON&a…...

智能体0门槛开发
分享一个智能体开发流程。 2025 年啊,好多专家还有行业报告都觉得这是智能体(AI Agent)应用的头一年。相关的应用在商业、工业、消费等好些领域都到了关键的时候,这意味着从实验室走向大规模实际应用的重要转变。而且呢࿰…...

ssh密钥登录GitHub时一直提示“Error: Permission denied (publickey)”
起因 环境:Windows10 背景:之前就是按照官方说明创建个rsa密钥,在git后台添加上,就行了,近期怎么添加怎么失败,总是“Error: Permission denied (publickey)”的提示! 尝试 各种尝试…...

mapbox加载geojson,鼠标移入改变颜色,设置样式以及vue中的使用
全国地图json数据下载地址 目录 html加载全部代码 方式一:使用html方式加载geojson 1. 初始化地图 2. 加载geojson数据 设置geojson图层样式,设置type加载数据类型 设置线条 鼠标移入改变颜色,设置图层属性,此处是fill-extru…...
考研机试题:打印日期
描述 给出年分m和一年中的第n天,算出第n天是几月几号。 输入描述: 输入包括两个整数y(1<y<3000),n(1<n<366)。 输出描述: 可能有多组测试数据,对于每组数据, 按 yyyy-mm-dd的格式将输入中对应的日期打印出来。 …...
OpenHarmonyOS 3.2 编译生成的hap和app文件的名称如何配置追加版本号?
找了一圈发现官方的文档都是最新的,3.2很多API都不支持,比如获取OhosAppContext,通过OhosAppContext来获取应用版本号,最终是通过读取app.json5的文件内容来读取版本号,最终修改entry下的hvigorfile.ts如下,…...
【openwrt】openwrt odhcpd配置介绍
odhcpd odhcpd是一个嵌入式DHCP/DHCPv6/RA服务器和NDP中继的进程,odhcpd是一个守护进程,用于服务和中继IP管理协议,以配置客户端和下游路由器。它试图遵循IPv6家用路由器的RFC 6204要求。odhcpd为DHCP、RA、无状态SLAAC和有状态DHCPv6、前缀委派提供服务器服务,并可用于在没…...

基于神经网络的视频编码NNVC(1):帧内预测
在H.266/VVC发布后,基于传统编码框架提升压缩率越来越难,随着深度学习的发展,研究人员开始尝试将神经网络引入编码器。为此,JVET工作组在2020年成立AHG11小组来专门进行基于神经网络的视频编码的研究。 为了方便研究,工…...

Android开发,待办事项提醒App的设计与实现
文章目录 1. 研究目的2. 主要内容3. 运行效果图4. 涉及到的技术点5. 开发环境6. 关于作者其它项目视频教程介绍 1. 研究目的 当今,随着时代的发展和计算机的普及,人们开始利用网络来记录并管理日常的事务,时下这方面的软件数不胜数。各种日程管理软件就是将每天的工作和事务安…...
豆瓣Top250电影的数据采集与可视化分析(scrapy+mysql+matplotlib)
文章目录 豆瓣Top250电影的数据采集与可视化分析(scrapy+mysql+matplotlib)写在前面数据采集(Visual Studio Code+Navicat)1.观察网页信息2.编写Scrapy代码(Visual Studio Code)2.1 创建Scrapy项目`doubanProject`2.2 创建爬虫脚本`douban.py`2.3 修改`douban.py`的代码2…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...