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

相关文章

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

揭秘世界上那些同时横跨两大洲的国家

我们在《世界人口过亿的一级行政区分布》盘点全球是那些人口过亿的一级行政区。 现在我们介绍五个横跨两州的国家,并整理七大洲和这些国家的KML矢量数据分析分享给大家,如果你需要这些数据,请在文末查看领取方式。 世界上横跨两大洲的国家 地球被分为七个大洲分别是亚洲、欧洲、北美洲、南美洲、非洲、大洋洲和南极洲。 七大洲示意图 其中,南极洲是无人居住的大陆,而其他六个大洲则孕育了众多国家和

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

poj 2187 凸包or旋转qia壳法

题意: 给n(50000)个点,求这些点与点之间距离最大的距离。 解析: 先求凸包然后暴力。 或者旋转卡壳大法。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <s

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。