OpenGL/GLUT实践:绘制旋转的立方体与雪人世界——添加光照与SOIL方式添加纹理(电子科技大学信软图形与动画Ⅱ实验)

本文主要是介绍OpenGL/GLUT实践:绘制旋转的立方体与雪人世界——添加光照与SOIL方式添加纹理(电子科技大学信软图形与动画Ⅱ实验),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

源码见GitHub:A-UESTCer-s-Code

文章目录

    • 1 运行效果
    • 2 实现过程
      • 2.1 几何转换
        • 2.1.1 窗口刷新
        • 2.1.2 绘制雪人场景
          • 2.1.2.1 绘制雪人
          • 2.1.2.2 绘制场景
        • 2.1.3 键盘事件
        • 2.1.4 运行效果
      • 2.2 颜色
      • 2.3 光照
        • 2.3.1 绘制正方体
        • 2.3.2 添加光源
      • 2.4 材质
        • 2.4.1 方法一
        • 2.4.2 方法二
      • 2.5 纹理
        • 2.5.1 SOIL环境配置
        • 2.5.2 纹理加载
      • 2.6 雪人世界光照与材质

1 运行效果

旋转的立方体实现效果:

image-20240417164843163

雪人世界实现效果:

recording

2 实现过程

2.1 几何转换

2.1.1 窗口刷新

利用透视变换实现窗口刷新:

  • 通过透视投影来设置窗口刷新函数,使用gluPerspective()函数定义透视投影。
void ChangeSize(GLsizei w, GLsizei h)
{GLfloat aspectRatio;if (h == 0)h = 1;glViewport(0, 0, w, h);glMatrixMode(GL_PROJECTION);glLoadIdentity();aspectRatio = (GLfloat)w / (GLfloat)h;gluPerspective(60.0f, aspectRatio, 1.0, 400.0);glMatrixMode(GL_MODELVIEW);glLoadIdentity();
}
2.1.2 绘制雪人场景
2.1.2.1 绘制雪人
  1. 绘制雪人身体部分:
    • 首先设置颜色为白色。
    • 使用glTranslatef()将当前矩阵沿着x、y和z轴移动到指定位置。
    • 绘制一个半径为0.75的实心球体作为雪人的身体。
  2. 绘制雪人头部:
    • 再次使用glTranslatef()将当前矩阵移动到头部位置。
    • 绘制一个半径为0.25的实心球体作为雪人的头部。
  3. 绘制雪人眼睛:
    • 将当前矩阵保存(使用glPushMatrix()),以便后续绘制完成后恢复到初始状态。
    • 设置眼睛颜色为黑色。
    • 分别用glTranslatef()将当前矩阵移动到左眼和右眼的位置。
    • 绘制半径为0.05的实心小球体作为眼睛。
    • 恢复之前保存的矩阵状态(使用glPopMatrix())。
  4. 绘制雪人的鼻子:
    • 设置鼻子颜色为橙红色。
    • 使用glRotatef()将当前矩阵绕着x轴旋转0度(这里没有实际的旋转操作)。
    • 绘制一个底半径为0.08、高度为0.5的圆锥体作为雪人的鼻子。
2.1.2.2 绘制场景
  1. 清除颜色和深度缓冲区:
    • 使用glClear()函数清除颜色缓冲区和深度缓冲区,以便开始渲染新的帧。
  2. 重置变换矩阵:
    • 使用glLoadIdentity()函数重置变换矩阵,以确保每一帧的绘制都是从一个空白状态开始的。
  3. 设置相机(镜头):
    • 使用gluLookAt()函数设置相机的位置和方向。函数的参数为相机位置(x, 1.0f, z),相机目标位置(x+lx, 1.0f, z+lz),以及相机的上方向(0.0f, 1.0f, 0.0f)
  4. 绘制地面:
    • 使用白色绘制地面,通过glColor3f()设置颜色。
    • 使用glBegin()glEnd()包裹的GL_QUADS模式绘制一个矩形地面。
  5. 绘制36个雪人:
    • 使用两层嵌套的for循环,在不同的位置调用drawSnowMan()函数来绘制36个雪人。
    • 内部的glPushMatrix()glPopMatrix()用于保存和恢复当前变换矩阵状态,以确保每个雪人的绘制都是相对独立的。
  6. 交换缓冲区:
    • 使用glutSwapBuffers()交换前后缓冲区,以显示渲染好的图像。
2.1.3 键盘事件
  1. 改变视线方向:
    • 当用户按下左右箭头键时,会改变角度变量angle的值,从而改变视线的方向。
    • 根据新的角度值重新计算视线向量的lxlz值,使用sincos函数将极坐标转换为平面坐标。
  2. 改变镜头位置:
    • 当用户按下上下箭头键时,会分别向前或向后移动镜头。
    • 根据lxlz向量以及给定的粒度(fraction)计算新的镜头位置(x, z),实现沿视线方向的移动。
2.1.4 运行效果

实现窗口刷新演示:

recording

键盘控制前后移动和左右转头:

recording

2.2 颜色

  1. 定义颜色方式:

    • OpenGL通过指定红、绿、蓝(RGB)成分的强度来定义颜色。

    • 使用glColor<x><t>(red, green, blue, alpha)

      函数来设置颜色,其中:

      • <x>表示参数的数量,可以是3(表示RGB颜色)或4(表示RGBA颜色,包括alpha通道);
      • <t>表示参数的数据类型。
  2. 着色模式(shading model):

    • 着色模式定义了图元内部的颜色渲染方式。
    • 默认情况下,OpenGL采用平滑着色模式(GL_SMOOTH)。当图元的顶点指定了不同的颜色时,OpenGL会在顶点之间进行平滑过渡,使得图元内部的颜色呈现渐变效果
    • 另一种着色模式是单调着色(GL_FLAT),在这种模式下,图元内部的颜色取决于最后一个顶点所指定的颜色。对于GL_POLYGON图元,内部颜色取决于第一个顶点的颜色。

GL_SMOOTH 来选择平滑,实现效果如下:

image-20240416220438503

GL_FLAT 单调着色模式,实现效果如下:

image-20240416220631363

2.3 光照

2.3.1 绘制正方体
  1. 全局变量:
    • xrotyrot:用于存储立方体绕x轴和y轴的旋转角度。
    • xspeedyspeed:用于控制立方体绕x轴和y轴的旋转速度。
    • z:用于控制立方体在z轴上的位置。
  2. changeSize函数:
    • 设置OpenGL视口,并根据窗口大小设置透视投影。
  3. InitGL函数:
    • 进行OpenGL的初始化设置,包括设置着色模式、清空颜色缓冲区和深度缓冲区等。
  4. renderScene函数:
    • 清空颜色缓冲区和深度缓冲区。
    • 重置模型视图矩阵,并移动相机位置到z轴为z的位置。
    • 根据xrotyrot的值进行旋转。
    • 绘制一个红色的立方体。
    • 利用双缓冲机制交换前后缓冲区,将绘制的图像显示在屏幕上。
    • 根据xspeedyspeed的值更新旋转角度。
  5. processSpecialKeys函数:
    • 处理特殊键盘按键事件,包括上下左右箭头键和Page Up/Page Down键,分别用于控制立方体在z轴上的移动和绕x轴、y轴的旋转速度。
  6. 主函数:
    • 初始化OpenGL和GLUT,并创建窗口。
    • 注册回调函数,包括绘制函数、窗口大小变化函数和键盘特殊按键事件处理函数。
    • 启用深度测试和双缓冲机制。
    • 进入主循环,等待事件的发生。

实现效果:

recording
2.3.2 添加光源
  1. 启用光源:

    • 在键盘按下“l”键时,调用glEnable(GL_LIGHTING);来启用光照计算。
  2. 设置光照模型:

    • 设置光源参数:在程序头部设置了光源的参数,包括环境光和漫反射光的强度和位置。

      • ambientLight[]环境光的强度,用来模拟场景中各处的间接光照。
      • diffuseLight[]漫反射光的强度,用来模拟光线直接照射到物体表面后的散射。
      • position[]:光源的位置,其中最后一个参数是1.0表示光源为定向光,0.0表示光源为点光源。
    • 设置并启用光照:

      InitGL函数中,调用glLight()函数来设置光源的参数,并启用光源GL_LIGHT0glLightfv函数用于设置光源的各个属性,包括环境光、漫反射光、镜面反射光和光源位置等。

最终效果:

recording

2.4 材质

2.4.1 方法一

使用 glMaterialfv函数手动设置材质属性。

  • 定义一个数组来指定物体表面的材质属性,例如GLfloat gray[] = {0.9f, 0.0f, 0.0f, 1.0f};表示物体表面反射90%的红光。
  • 使用glMaterialfv函数设置材质属性,例如glMaterialfv(GL_FRONT, GL_DIFFUSE, gray);用于设置散射光属性。

实现效果:

image-20240417144747792
2.4.2 方法二

使用颜色追踪(Color Tracking)来设置材质属性。

  • 调用glColorMaterial函数启用颜色追踪,例如glColorMaterial(GL_FRONT, GL_DIFFUSE);表示追踪正面的散射光属性。
  • 启用颜色追踪功能,使用glEnable(GL_COLOR_MATERIAL);
  • 使用glColor函数设置物体的颜色,例如glColor(0.0f, 0.0f, 0.9f, 1.0f);表示设置物体为蓝色。

实现效果:

image-20240417145056781

2.5 纹理

2.5.1 SOIL环境配置
  1. 首先在项目目录下创建libinclude文件夹,分别将SOIL.libSOIL.h放入。

  2. 在VS2022的项目中打开项目属性页,将如下两项加入刚刚创建的两个目录。

    image-20240417163446630
  3. 在链接器的常规中,加入lib目录。

    image-20240417163534482
  4. 在链接器的输入中,加入静态库的完整名称。

    image-20240417163611310
2.5.2 纹理加载
  1. LoadGLTextures函数:

    • 使用循环加载两张图片作为纹理,分别存储在texture[0]texture[1]中。

    • 调用SOIL_load_OGL_texture函数加载图片并将其转换为OpenGL纹理。该函数的参数包括图片路径、加载方式、生成新的纹理ID以及其他标志。

    • 检查纹理加载是否成功,如果失败则返回false。

    • 对每张纹理进行绑定,并设置放大和缩小过滤器为线性过滤器(GL_LINEAR)。

  2. renderScene函数

    绘制立方体的各个面:

    • 每个面都使用glBegin(GL_QUADS)开始绘制,并使用glEnd()结束。
    • 每个面的顶点坐标都使用glVertex3f指定。
    • 每个顶点的纹理坐标都使用glTexCoord2f指定,以便纹理正确贴在立方体上。
    • 每个面的法线(用于光照计算)都使用glNormal3f指定。

2.6 雪人世界光照与材质

要在雪人世界加入光照与材质,我们只需要加入一个InitGL函数,进行光照初始化;并加入普通按键控制,实现按l时, 通过设置glDisable(GL_LIGHTING);glEnable(GL_LIGHTING);,就可以打开/关闭光照。

同时,为了保持在光照下,颜色保持不变,我们只需要加入简单的两行代码使用glColorMaterialglEnable函数,即可实现颜色追踪(Color Tracking)来设置材质属性。

int InitGL(GLvoid)
{glColorMaterial(GL_FRONT, GL_DIFFUSE);glEnable(GL_COLOR_MATERIAL);GLfloat ambientLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };GLfloat diffuseLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };GLfloat position[] = { 0.0f, 0.0f, 2.0f, 1.0f };glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);glLightfv(GL_LIGHT0, GL_POSITION, position);glEnable(GL_LIGHT0);return true;
}

这篇关于OpenGL/GLUT实践:绘制旋转的立方体与雪人世界——添加光照与SOIL方式添加纹理(电子科技大学信软图形与动画Ⅱ实验)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131325

相关文章

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

PHP应用中处理限流和API节流的最佳实践

《PHP应用中处理限流和API节流的最佳实践》限流和API节流对于确保Web应用程序的可靠性、安全性和可扩展性至关重要,本文将详细介绍PHP应用中处理限流和API节流的最佳实践,下面就来和小编一起学习... 目录限流的重要性在 php 中实施限流的最佳实践使用集中式存储进行状态管理(如 Redis)采用滑动

ShardingProxy读写分离之原理、配置与实践过程

《ShardingProxy读写分离之原理、配置与实践过程》ShardingProxy是ApacheShardingSphere的数据库中间件,通过三层架构实现读写分离,解决高并发场景下数据库性能瓶... 目录一、ShardingProxy技术定位与读写分离核心价值1.1 技术定位1.2 读写分离核心价值二

HTTP 与 SpringBoot 参数提交与接收协议方式

《HTTP与SpringBoot参数提交与接收协议方式》HTTP参数提交方式包括URL查询、表单、JSON/XML、路径变量、头部、Cookie、GraphQL、WebSocket和SSE,依据... 目录HTTP 协议支持多种参数提交方式,主要取决于请求方法(Method)和内容类型(Content-Ty

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired

MySQL分库分表的实践示例

《MySQL分库分表的实践示例》MySQL分库分表适用于数据量大或并发压力高的场景,核心技术包括水平/垂直分片和分库,需应对分布式事务、跨库查询等挑战,通过中间件和解决方案实现,最佳实践为合理策略、备... 目录一、分库分表的触发条件1.1 数据量阈值1.2 并发压力二、分库分表的核心技术模块2.1 水平分

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解