计算机图形学:实验三 光照与阴影
一、程序功能设计
设置了一个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…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
