Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射

系列文章

一、逆向工程
Sketchup 逆向工程(一)破解.skp文件数据结构
Sketchup 逆向工程(二)分析三维模型数据结构
Sketchup 逆向工程(三)软件逆向工程从何处入手
Sketchup 逆向工程(四)破解的乐趣 钩子 外挂 代码注入

二、OpenGL渲染模型
Python+OpenGL绘制3D模型(一)Python 和 PyQt环境搭建
Python+OpenGL绘制3D模型(二)程序框架PyQt5
Python+OpenGL绘制3D模型(三)程序框架PyQt6
Python+OpenGL绘制3D模型(四)绘制线段
Python+OpenGL绘制3D模型(五)绘制三角型
Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射
Python+OpenGL绘制3D模型(七)制作3dsmax导出插件
Python+OpenGL绘制3D模型(八)绘制插件导出的插件
Python+OpenGL绘制3D模型(九)完善插件功能: 矩阵,材质,法线
Python+OpenGL 杂谈(一)

三、成果
疫情期间关在家里实在没事干,破解了Sketchup,成功做出可以读取并显示.skp文件的程序SuViewer

前言

Sketchup作为目前设计院最为流行的设计软件(非工程制图软件),深受设计师的喜爱,软件小巧,而功能强大,有不少为之开发的插件应运而生,不过呢,关于底层数据结构和工作原理相关的文章少之又少,本文意在填补一下这方面的空缺,通过逆向软件分析,展示软件内部奥秘。本文用到的工具:IDA Pro,Immunity Debugger,Visual Studio (逆向工程三件套)数据结构属于知识产权的核心机密:


Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射

运行效果:
在这里插入图片描述

文章目录

  • 系列文章
  • 前言
  • Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射
    • 一、从文件读取贴图
    • 二、glBindTexture
    • 三、指定贴图坐标
    • 四、运行图效果
    • 五、2个问题和原因
    • 六、源代码
      • 1、Draw1.py
      • 2、tOpenGLqt5.py
  • 系列文章预告

一、从文件读取贴图

记得以前用c++写的时候,要编译链接图像库,用于对应图片格式的加载,每个用到的格式都要单独搞一遍,在网上找开源的图片库,下载,编译,测试,一套下来搞的人很累,现在,因为有Qt的加持,载入贴图变得非常简单,全部交给Qt来做,只需要几行代码

def load_texture_from_file( filepath ):with open(filepath, 'rb') as hf:data = hf.read()image = QImage()valid = image.loadFromData(data)if not valid:return Falsegl_tex_obj = QOpenGLTexture(image.mirrored())return gl_tex_obj

图片载入到Qt后,还需要调用OpenGL的库加载贴图数据,另外Qt的图片坐标Y轴是向下增加的,符合显示屏幕坐标的习惯,而OpenGL中的Y轴坐标是正常向上的,所以Y轴需要mirror处理一下

is_built = False
gl_tex = Nonedef draw(gl):global is_built, gl_texif not is_built:gl_tex = load_texture_from_file("c:/temp/cg.jpg")is_built = True

为了代码保持简洁,简化接口,不要把简单的事情复杂化:
1、载入贴图后,对象保存到全局对象中,
2、载入模型的初始工作也内嵌到draw函数中执行,这种思维在项目越做越大也仍然实用
3、贴图路径使用了硬编码方式的绝对路径,在没有形成一个模型框架的时候,暂且用这种方式对测试减少不少工作量

演示用到的贴图文件下载:
在这里插入图片描述

二、glBindTexture

在绘制模型前,设置一下OpenGL的状态机,调用glBindTexture指定当前纹理单元的数据,然后调用glEnable(GL_TEXTURE)激活纹理单元,因为有贴图作为像素的输入颜色,所以颜色设为纯白

    gl.glDisable(gl.GL_TEXTURE_2D)gl.glColor3f(0.9, 0.83, 0.6)# 绘制上下2个面draw_single_face(p1, p2, p3, p4, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p5, p6, p7, p8, uv1,  uv2,  uv3,  uv4,  gl)gl_tex.bind()gl.glEnable(gl.GL_TEXTURE_2D)gl.glColor3f(1, 1, 1)# 绘制其他4个面,前,后,左,有draw_single_face(p1, p2, p6, p5, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p3, p4, p8, p7, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p2, p3, p7, p6, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p4, p1, p5, p8, uv1,  uv2,  uv3,  uv4, gl)

在本例中,前后左右的面映射了贴图,上下2个面仍然用了原来的模型颜色(淡黄色)

三、指定贴图坐标

在调用glVertex3f之前,需要先指定顶点的贴图坐标属性,调用glTexCoord2f

    gl.glBegin(gl.GL_TRIANGLES)# 第一个顶点gl.glTexCoord2f( ... )gl.glVertex3f( ... )# 第二个顶点gl.glTexCoord2f( ... )gl.glVertex3f( ... )# 第三个顶点gl.glTexCoord2f( ... )gl.glVertex3f( ... )# 完成gl.glEnd()

四、运行图效果

在这里插入图片描述

五、2个问题和原因

第一、模型的位置有点高:

这是应为camera坐标系的中心为(0,0,0), 而模型高度是从 0.0 到 2.0,要调整一下中心坐标,可以通过设置概念上的world matrix沿Z轴往下移动1.0个单位,模型就居中了,目前程序框架里控制视角的平移,也可以通过这个方法来解决,不过我还是打算在之后引入Camera对象的计算,计算的逻辑思维更清晰,不容易出错

    # Cameraself.gl.glMatrixMode(self.gl.GL_MODELVIEW)self.gl.glLoadIdentity()self.gl.glTranslatef(0.0,0.0,-self.zoom)self.gl.glRotatef(self.rotX-90,1.0,0.0,0.0)self.gl.glRotatef(self.rotZ,0.0,0.0,1.0)# Worldself.gl.glTranslatef(0.0,0.0,-1.0)  # 在这里加上对world matrix的改变

这里的矩阵叠加,是按照相反的顺序乘积的,比较容易搞错,OpenGL中有个机制 PushMatrix PopMatrix,也就是说后面加入的矩阵,可以通过PopMatrix来恢复到之前的矩阵乘积的状态,越靠后面加进来的矩阵,代表子物体的矩阵,在运算中最先乘这个矩阵

第二、当模型放大的时候,近的地方会被切掉

调整下几行代码,

    # Projectionself.gl.glMatrixMode(self.gl.GL_PROJECTION)pm = QMatrix4x4()aspectRatio = w/hfov = 45 / aspectRatio if w < h else 45pm.perspective(fov,  w/h,  2,  5000)self.gl.glLoadMatrixf(pm.data())

这里是设置透视投影矩阵的代码,pm.perspective(fov, w/h, 2, 5000),这个函数调用的最后2个参数,分别代表了近裁剪平面(z-buff=0.0),和远裁剪平面(z-buff=1.0),这2个值明显不太匹配当前场景的大小,每次应该根据当前场景大小来适当选择取值范围

    pm.perspective(fov,  w/h,  0.1,  100)

改成 0.1 到 100 的范围,就能够比较适配当前的测试模型
在这里插入图片描述
到此我们已经能够绘制一个完整的模型,不过模型数据来源还没解决,不能显示复杂的模型,下节我们要讲一个模型数据来源的通用方法,通过编写一个3dsmax插件导出模型,因为3dsmax也支持python,所以下节我们用很少的python代码来完成一个复杂的模型导出插件

六、源代码

1、Draw1.py

from PyQt5.QtGui import QVector3D, QVector2D, QImage,  QOpenGLTexture################################
#                   FILE DESCRIPTION
#  文件描述:load_texture_and_bind()
#  对应文章:Python+OpenGL绘制3D模型(六) 载入贴图及映射到模型
#  作者:李航 Lihang
#
################################is_built = False
gl_tex = Nonedef draw(gl):global is_built, gl_texif not is_built:gl_tex = load_texture_from_file("c:/temp/cg.jpg")is_built = True# 设置z-buff偏移gl.glEnable(gl.GL_POLYGON_OFFSET_FILL)gl.glPolygonOffset(1, 1)# 绘制填充面draw_box_faces(gl_tex, gl)# 关闭z-buff偏移gl.glDisable(gl.GL_POLYGON_OFFSET_FILL)# 绘制线框gl.glColor3f(0.0, 0.0, 0.0)draw_box_lines(gl)def load_texture_from_file( filepath ):with open(filepath, 'rb') as hf:data = hf.read()image = QImage()valid = image.loadFromData(data)if not valid:return Falsegl_tex_obj = QOpenGLTexture(image.mirrored())return gl_tex_obj############
# draw_box_faces
#   画面 - 中间的填充部分
############
def draw_box_faces(gl_tex, gl):p1 = QVector3D(-1, -1, 0 )p2 = QVector3D(+1, -1, 0 )p3 = QVector3D(+1, +1, 0 )p4 = QVector3D(-1, +1, 0 )p5 = QVector3D(-1, -1, 2 )p6 = QVector3D(+1, -1, 2 )p7 = QVector3D(+1, +1, 2 )p8 = QVector3D(-1, +1, 2 )uv1 = QVector2D(0, 0)uv2 = QVector2D(1, 0)uv3 = QVector2D(1, 1)uv4 = QVector2D(0, 1)gl.glDisable(gl.GL_TEXTURE_2D)gl.glColor3f(0.9, 0.83, 0.6)draw_single_face(p1, p2, p3, p4, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p5, p6, p7, p8, uv1,  uv2,  uv3,  uv4,  gl)gl_tex.bind()gl.glEnable(gl.GL_TEXTURE_2D)gl.glColor3f(1, 1, 1)draw_single_face(p1, p2, p6, p5, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p3, p4, p8, p7, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p2, p3, p7, p6, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p4, p1, p5, p8, uv1,  uv2,  uv3,  uv4, gl)def draw_single_face(p1, p2, p3, p4, uv1,  uv2,  uv3,  uv4, gl):gl.glBegin(gl.GL_TRIANGLES)gl.glTexCoord2f(uv1.x(), uv1.y())gl.glVertex3f(p1.x(),  p1.y(), p1.z())gl.glTexCoord2f(uv2.x(), uv2.y())gl.glVertex3f(p2.x(),  p2.y(), p2.z())gl.glTexCoord2f(uv3.x(), uv3.y())gl.glVertex3f(p3.x(),  p3.y(), p3.z())gl.glEnd()gl.glBegin(gl.GL_TRIANGLES)gl.glTexCoord2f(uv3.x(), uv3.y())gl.glVertex3f(p3.x(),  p3.y(), p3.z())gl.glTexCoord2f(uv4.x(), uv4.y())gl.glVertex3f(p4.x(),  p4.y(), p4.z())gl.glTexCoord2f(uv1.x(), uv1.y())gl.glVertex3f(p1.x(),  p1.y(), p1.z())gl.glEnd()############
# draw_box_lines
#   画面 - 外侧的线
############
def draw_box_lines(gl):p1 = QVector3D(-1, -1, 0 )p2 = QVector3D(+1, -1, 0 )p3 = QVector3D(+1, +1, 0 )p4 = QVector3D(-1, +1, 0 )p5 = QVector3D(-1, -1, 2 )p6 = QVector3D(+1, -1, 2 )p7 = QVector3D(+1, +1, 2 )p8 = QVector3D(-1, +1, 2 )# 一个立方体有12条边draw_single_line( p1, p2, gl )draw_single_line( p2, p3, gl )draw_single_line( p3, p4, gl )draw_single_line( p4, p1, gl )draw_single_line( p5, p6, gl )draw_single_line( p6, p7, gl )draw_single_line( p7, p8, gl )draw_single_line( p8, p5, gl )draw_single_line( p1, p5, gl )draw_single_line( p2, p6, gl )draw_single_line( p3, p7, gl )draw_single_line( p4, p8, gl )def draw_single_line(p1, p2, gl):gl.glBegin(gl.GL_LINES)gl.glVertex3d(p1.x(),  p1.y(), p1.z())gl.glVertex3d(p2.x(),  p2.y(), p2.z())gl.glEnd()

2、tOpenGLqt5.py

import sys
from PyQt5.QtCore import (QPoint)
from PyQt5.QtGui import (QMatrix4x4, QVector3D, QOpenGLVersionProfile)
from PyQt5.QtWidgets import QApplication, QOpenGLWidgetfrom Draw1 import draw############
# GLWidget
# OpenGL 窗口通用程序框架
#   1、创建OpenGL环境
#   2、设置矩阵
#   3、控制窗口视角
#   4、调用 draw 绘图主函数
############
class GLWidget(QOpenGLWidget):def __init__(self, parent):super(GLWidget, self).__init__( parent)self.dragPressPos = QPoint()self.rotX=45self.rotZ=0self.ps_button = 0self.ps_rotX = 0self.ps_rotZ = 0self.zoom=10############# 创建OpenGL环境# Qt6 和 Qt5的主要区别在这里############def initializeGL(self):version_profile = QOpenGLVersionProfile()version_profile.setVersion(2, 0)self.gl = self.context().versionFunctions(version_profile)self.gl.initializeOpenGLFunctions()############# paintEvent############def paintEvent(self,  event):# Step 0self.makeCurrent()# Step 1self.gl.glClearColor(0.85, 0.85, 0.85, 1.0)self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)self.gl.glEnable(self.gl.GL_DEPTH_TEST)# Step 2self.SetupMatrix()# Step 3draw(self.gl)#self.drawTarget(self.gl)############# 绘图# 这里是个绘图的简单测试代码############def drawTarget(self, gl):p = QVector3D(0, 0, 0)gl.glColor3f(1.0, 0.0, 0.0);gl.glBegin(gl.GL_LINES)gl.glVertex3d(p.x()-1,  p.y(),  p.z()  )gl.glVertex3d(p.x()+1,  p.y(),  p.z() )gl.glEnd()gl.glColor3f(0.0, 1.0, 0.0);gl.glBegin(gl.GL_LINES)gl.glVertex3d(p.x(),  p.y()-1,  p.z()  )gl.glVertex3d(p.x(),  p.y()+1,  p.z() )gl.glEnd()gl.glColor3f(0.0, 0.0, 1.0);gl.glBegin(gl.GL_LINES)gl.glVertex3d(p.x(),  p.y(),  p.z()  )gl.glVertex3d(p.x(),  p.y(),  p.z() +4 )gl.glEnd()############# 设置矩阵# 透视矩阵和Camera矩阵############def SetupMatrix(self):# ViewPortw = self.width()h = self.height()self.gl.glViewport(0, 0, w, h)  # Projectionself.gl.glMatrixMode(self.gl.GL_PROJECTION)pm = QMatrix4x4()aspectRatio = w/hfov = 45 / aspectRatio if w < h else 45pm.perspective(fov,  w/h,  0.1,  100)self.gl.glLoadMatrixf(pm.data())# Cameraself.gl.glMatrixMode(self.gl.GL_MODELVIEW)self.gl.glLoadIdentity()self.gl.glTranslatef(0.0,0.0,-self.zoom)self.gl.glRotatef(self.rotX-90,1.0,0.0,0.0)self.gl.glRotatef(self.rotZ,0.0,0.0,1.0)# Worldself.gl.glTranslatef(0.0,0.0,-1)  # 在这里加上对world matrix的改变############# 视角控制# 1、左键旋转# 2、中间缩放# 3、平移 **TODO**############def mousePressEvent(self,event):self.dragPressPos = event.pos()self.ps_button = event.button()self.ps_rotX = self.rotXself.ps_rotZ = self.rotZdef mouseMoveEvent(self, event):diff = event.pos() - self.dragPressPosif self.ps_button == 1:self.rotX = self.ps_rotX + diff.y()*0.5if self.rotX > 90:self.rotX = 90if self.rotX < -90:self.rotX = -90;            # rotZself.rotZ = self.ps_rotZ + diff.x()*0.5self.repaint()def wheelEvent(self, event):delta = event.angleDelta().y()if delta < 0 :self.zoom += self.zoom * 0.2else:self.zoom -= self.zoom * 0.2self.repaint()############
# App
# 创建主窗口应用程序
# 并且进入消息循环
############
if __name__ == '__main__':app = QApplication(sys.argv)widget = GLWidget(None)widget.resize(640, 480)widget.show()sys.exit(app.exec())

系列文章预告

目标是一个完善的Viewer,能够显示Sketchup的.skp文件中的3D模型
在这里插入图片描述

Corona渲染器照片级渲染效果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/314540.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【openlayers-5】地图点、线、面等要素添加

1、添加点 //创建一个点 var point new ol.Feature({geometry: new ol.geom.Point([117.2, 35.8] ),}) //设置点的样式信息 point.setStyle(new ol.style.Style({//填充色fill: new ol.style.Fill({color: rgba(255, 255, 255, 0.2),}),//边线颜色stroke: new ol.style.Strok…

主成分分析(PCA):探索数据的核心

文章目录 前言1. 什么是 PCA &#xff1f;2. PCA 的原理2.1 协方差和方差2.2 核心思想2.3 步骤 3. PCA 的应用场景4. PCA 的优缺点5. 示例&#xff1a;人脸识别5.1 完整代码5.2 运行结果 结语 前言 当今社会&#xff0c;数据无处不在。从社交媒体到金融交易&#xff0c;从医疗…

Valheim英灵神殿2456-2457-2458端口TCP和UDP开通

Valheim英灵神殿游戏需要开启云服务器2456、2457和2458三个端口&#xff0c;端口的TCP和UDP协议均要开通&#xff0c;云服务器吧yunfuwuqiba.com分享来详细说下Valheim英灵神殿游戏服务器端口说明&#xff1a; Valheim英灵神殿服务器端口 Valheim英灵神殿游戏要使用云服务器的…

以元旦为题的诗词(三)

愿新的一年给我们带来无尽的好运和幸福愿我们的梦想在新的起点绽放! 让我们在未来的日子里书写新的篇章! 接着分享几首以元旦为题的几首诗&#xff0c;喜欢的朋友可以自取&#xff0c;想要更多免费的诗词&#xff0c;请自行百度或小程序搜索&#xff1a;美诗计 元旦 元旦佳节…

Nginx(十五) proxy_pass和proxy_redirect指令的组合测试

Nginx反向代理配置文件参数详解请参考 Nginx(十三) 配置文件详解 - 反向代理&#xff08;超详细&#xff09; 测试1&#xff1a;proxy_redirect http://127.0.0.1:8080/three/ http://www.read*******l.cn:8688/four/; http {server {listen 8688;server_name www.read****…

Ps:亮度蒙版 - 混合颜色带方法

所谓“亮度蒙版”&#xff0c;就是根据图像的明暗程度进行选区并建立蒙版&#xff0c;这样便于对图像上进行分级调色。 Photoshop 支持众多的第三方亮度蒙版插件。如&#xff0c;TKActions、Lumenzia、ADP Pro、Raya Pro、LIM、EasyPanel、Introducing InstaMask等等。如此多的…

【C语言】Windows上用GTK写GUI程序

要使用GTK开发一个Windows图形用户界面程序&#xff0c;需要首先设置GTK开发环境。这通常包括安装GTK库和它的依赖&#xff0c;以及配置编译器和工具链。可以选择使用纯C语言和GTK库或者使用支持GTK绑定的其他语言&#xff0c;如Python、C或Rust。 1. 安装GTK开发库 在Window…

JVM 常用知识和面试题

1. 什么是JVM内存结构&#xff1f; jvm将虚拟机分为5大区域&#xff0c;程序计数器、虚拟机栈、本地方法栈、java堆、方法区&#xff1b; 程序计数器&#xff1a;线程私有的&#xff0c;是一块很小的内存空间&#xff0c;作为当前线程的行号指示器&#xff0c;用于记录当前虚拟…

阿里云服务器开放端口Oracle 1521方法教程

阿里云服务器ECS端口是在安全组设置的&#xff0c;Oracle数据库1521端口号开放是在安全组中添加规则来实现的&#xff0c;阿里云服务器网aliyunfuwuqi.com来详细说下阿里云服务器开放Oracle 1521端口方法教程&#xff1a; 阿里云服务器开放Oracle 1521端口 在阿里云服务器ECS…

2023年“中银杯”安徽省网络安全B模块(部分解析)

前言 以下是2023年中银杯安徽省网络安全B模块题目&#xff0c;镜像可以私聊我 B模块安全事件响应/网络安全数据取证/应用安全&#xff08;400 分&#xff09; B-1&#xff1a;CMS网站渗透测试 任务环境说明&#xff1a; √服务器场景&#xff1a;Server2206&#xff08;关…

echarts手动触发气泡的显示和隐藏

点击echarts图表后将点击的那个进行突出显示 <template><div id"demo"> </div><el-button type"primary" click"set">设置</el-button><el-button type"primary" click"cancel">取消&…

阶段十-分布式-nginx服务器

一、Nginx简介 Nginx 是高性能的 HTTP 和反向代理的服务器&#xff0c;处理高并发能力是十分强大的&#xff0c;能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数。tomcat并发数量理论值是500&#xff0c;实际也就300左右。 1.2 正向代理 正向代理代理的是客户…