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

相关文章

Golang中拼接字符串的6种方式性能对比

《Golang中拼接字符串的6种方式性能对比》golang的string类型是不可修改的,对于拼接字符串来说,本质上还是创建一个新的对象将数据放进去,主要有6种拼接方式,下面小编就来为大家详细讲讲吧... 目录拼接方式介绍性能对比测试代码测试结果源码分析golang的string类型是不可修改的,对于拼接字

Linux下修改hostname的三种实现方式

《Linux下修改hostname的三种实现方式》:本文主要介绍Linux下修改hostname的三种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下修改ho编程stname三种方式方法1:修改配置文件方法2:hFvEWEostnamectl命

SpringBoot接收JSON类型的参数方式

《SpringBoot接收JSON类型的参数方式》:本文主要介绍SpringBoot接收JSON类型的参数方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、jsON二、代码准备三、Apifox操作总结一、JSON在学习前端技术时,我们有讲到过JSON,而在

Spring Security注解方式权限控制过程

《SpringSecurity注解方式权限控制过程》:本文主要介绍SpringSecurity注解方式权限控制过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、摘要二、实现步骤2.1 在配置类中添加权限注解的支持2.2 创建Controller类2.3 Us

SpringBoot操作MaxComputer方式(保姆级教程)

《SpringBoot操作MaxComputer方式(保姆级教程)》:本文主要介绍SpringBoot操作MaxComputer方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的... 目录引言uqNqjoe一、引入依赖二、配置文件 application.properties(信息用自己

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

Nginx实现高并发的项目实践

《Nginx实现高并发的项目实践》本文主要介绍了Nginx实现高并发的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用最新稳定版本的Nginx合理配置工作进程(workers)配置工作进程连接数(worker_co

java两个List的交集,并集方式

《java两个List的交集,并集方式》文章主要介绍了Java中两个List的交集和并集的处理方法,推荐使用Apache的CollectionUtils工具类,因为它简单且不会改变原有集合,同时,文章... 目录Java两个List的交集,并集方法一方法二方法三总结java两个List的交集,并集方法一

Python中如何控制小数点精度与对齐方式

《Python中如何控制小数点精度与对齐方式》在Python编程中,数据输出格式化是一个常见的需求,尤其是在涉及到小数点精度和对齐方式时,下面小编就来为大家介绍一下如何在Python中实现这些功能吧... 目录一、控制小数点精度1. 使用 round() 函数2. 使用字符串格式化二、控制对齐方式1. 使用

Nginx配置系统服务&设置环境变量方式

《Nginx配置系统服务&设置环境变量方式》本文介绍了如何将Nginx配置为系统服务并设置环境变量,以便更方便地对Nginx进行操作,通过配置系统服务,可以使用系统命令来启动、停止或重新加载Nginx... 目录1.Nginx操作问题2.配置系统服android务3.设置环境变量总结1.Nginx操作问题