碎碎念

由于课设和大创涉及到了模型的旋转,因此专门去学习了模型的导入,也是废了不少心思,现在总结一下两种格式的简单导入,以及对stl模型两种格式的简单介绍。网上有很多大佬都有详细的解答,结尾附上链接;在导入较大网格数的模型时发现qt不是运行不了就是加载时间很长,网上找了很多办法也没有解决,并且当面片数过高时,由于二进制文件的三角形面片数占4字节(最多65535个三角形),因此超出时便会报溢出异常。本人太菜不知道怎么解决,只好采取blender的精简修改器减少面片数,当然面片数过低模型精度会降低,希望有大佬能解答疑问。欢迎指正。

下面是效果图展示:

(ps:模型来自Three D Scans)

1. stl格式介绍及导出

STL文件:STL文件是一种用许多空间小三角形面片逼近三维实体表面的数据模型,STL模型的数据通过给出组成三角形法向量的3个分量(用于确定三角面片的正反方向)及三角形的3个顶点坐标来实现,一个完整的STL文件记载了组成实体模型的所有三角形面片的法向量数据和顶点坐标数据信息。目前的STL文件格式包括二进制文件(BINARY)和文本文件(ASCII)两种。

1.1 ascll码格式

1.2 二进制格式

1.3 模型导出

模型可以自己在建模软件中制作或者去建模软件下载stl格式,这里以blender为例:

1. 首先打开blender,点击你想要导出的物体,注意在OpenGL中为右手坐标系,z轴朝外,如果发现方向不对需要将模型导入建模软件中调整一下模型方向再导出;

导出模型时最好以模型原点为中心,旋转时将以模型中心旋转;

2. 点击文件,导出,选择stl格式;导出模型时注意点击仅导出选中物体,可根据需要选择是否导出ascll码格式,默认导出二进制格式。

这样就成功导出stl模型文件了。

1.4 举例解释两种格式文件

以上方导出的正方体为例解释:

显然正方体有六个面,每个面由两个等腰直角三角形拼接而成,故一共有2*6=12个三角形,即下图这样的三角形有12组

下面使用二进制查看器查看机器码:(或者用qt的资源文件打开也可查看)

前八十字节记录了模型基本信息,即模型是从blender导出的;

紧接着四字节为三角形面片信息;

剩下对于每个三角形有3个四字节浮点数(面片法矢量),3个顶点坐标(每个坐标为4字节的三元组)最后两个字节描述三角形面片属性信息。即每个三角形占50字节(3*4 + 3*(3*4)=50)

2 QOpenGLWidget类进行opengl绘图

基础准备

  1. 在qt中新建一个c++类,继承QWidget;
  2. 接着在该类导入相应的头文件
    #include <QOpenGLWidget>
    #include<QVector>
    #include<QOpenGLExtraFunctions>
    #include<QOpenGLShaderProgram>
    #include<QOpenGLVertexArrayObject>
    #include<QOpenGLBuffer>
    #include<QMatrix3x3>
    #include<QMatrix4x4>
    #include<QMouseEvent>
    #include<QWheelEvent>
  3. 将demoOpenglWidget的继承类改为
    class demoOpenglWidget : public QOpenGLWidget,public QOpenGLExtraFunctions

    点击QOpenGLWidget头文件,找到下面三个函数进行重载:

    protected:
    //建立OpenGL的资源和状态。在第一次调用resizeGL()或paintGL()之前调用一次void initializeGL();
    //设置OpenGL视口,投影等。每当调整Widget的大小时(第一次显示窗口Widget时会调用它,因为所有新创建Widget都会自动获得调整大小的事件)void resizeGL(int width, int height);
    //渲染OpenGL场景,需要更新Widget时就会调用void paintGL();
  4. demoOpenglWidget.h

    #ifndef DEMOOPENGLWIDGET_H
    #define DEMOOPENGLWIDGET_H#include <QOpenGLWidget>
    #include<QVector>
    #include<QOpenGLExtraFunctions>
    #include<QOpenGLShaderProgram>
    #include<QOpenGLVertexArrayObject>
    #include<QOpenGLBuffer>
    #include<QMatrix3x3>
    #include<QMatrix4x4>
    #include<QMouseEvent>
    #include<QWheelEvent>class demoOpenglWidget : public QOpenGLWidget,public QOpenGLExtraFunctions
    {Q_OBJECT
    public:explicit demoOpenglWidget(QWidget *parent = nullptr);~demoOpenglWidget();
    public:double rotate_y=0;double rotate_x=0;double rotate_z=0;
    protected:void initializeGL();void resizeGL(int width, int height);void paintGL();QVector<float> loadAscllStl(QString filename,int ratio);//加载Ascll格式的stl文件QVector<float> loadBinStl(std::string filename, int ratio);//加载二进制格式的stl文件void mousePressEvent(QMouseEvent *event);void mouseMoveEvent(QMouseEvent *event);void wheelEvent(QWheelEvent *event);//图片缩放功能private:QVector<float> vertices;QVector<float> Position;//顶点位置QVector<float> Normal;//法向量QOpenGLShaderProgram shaderprogram;QOpenGLVertexArrayObject VAO;QOpenGLBuffer VBO;//MVPQMatrix4x4 model;QMatrix4x4 view;QMatrix4x4 projection;//    int verticesCnt;GLfloat xtrans,ytrans,ztrans;QVector2D mousePos;QQuaternion rotation;
    };#endif // DEMOOPENGLWIDGET_H
    
  5. 在qt资源中导入顶点着色器、片段着色器,并重载initializeGL、resizeGL和paintGL。
    //stl.vert
    #version 330 core
    layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0
    layout (location = 1) in vec3 aNormal; // 颜色变量的属性位置值为 1uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;out vec3 FragPos;
    out vec3 Normal;void main()
    {gl_Position = projection * view * model * vec4(aPos, 1.0);Normal = mat3(model) * aNormal;//用于旋转时,使得法向量一起改变FragPos = vec3(model * vec4(aPos, 1.0));
    }//stl.frag
    #version 330 core
    //layout( location = 0 ) out vec4 FragColor;out vec4 FragColor;
    uniform vec3 objectColor;
    uniform vec3 lightColor;
    in vec3 FragPos;
    in vec3 Normal;
    uniform vec3 lightPos;void main()
    {float ambientStrength = 0.1;vec3 ambient = ambientStrength * lightColor;vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = diff * lightColor;vec3 result = (ambient + diffuse) * objectColor;FragColor = vec4(result, 1.0);}
    //渲染OpenGL场景,widget需要更新时调用
    void demoOpenglWidget::initializeGL()
    {this->initializeOpenGLFunctions();shaderprogram.create();//将着色器加载到shaderprogram小程序中(注意路径根据自己的路径名称修改)
    if(!shaderprogram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/stl.vert"))qDebug()<<"ERROR:"<<shaderprogram.log();if(!shaderprogram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/stl.frag"))qDebug()<<"ERROR:"<<shaderprogram.log();    //如果编译出错,打印报错信息//将添加到此程序的着色器与addshader链接在一起if(!shaderprogram.link())qDebug()<<"ERROR:"<<shaderprogram.log();    //如果链接出错,打印报错信息//创建VAO、VBO对象并绑定VAO.create();VAO.bind();VBO.create();VBO.bind();VBO.allocate(vertices.data(),sizeof(float)*vertices.size());//向着色器中传入顶点、法向量属性信息shaderprogram.setAttributeBuffer("aPos",GL_FLOAT,0,3,sizeof(GLfloat)*6);shaderprogram.enableAttributeArray("aPos");shaderprogram.setAttributeBuffer("aNormal", GL_FLOAT,sizeof(GLfloat) * 3, 3, sizeof(GLfloat) * 6);shaderprogram.enableAttributeArray("aNormal");this->glEnable(GL_DEPTH_TEST);view.setToIdentity();view.lookAt(QVector3D(0.0f,0.0f,3.0f),QVector3D(0.0f,0.0f,0.0f),QVector3D(0.0f,1.0f,0.0f));}//设置OpenGL视口、投影等。
    void demoOpenglWidget::resizeGL(int w, int h)
    {this->glViewport(0,0,w,h);projection.setToIdentity();projection.perspective(60.0f,(GLfloat)w/(GLfloat)h, 0.001f, 100.0f);//视角-宽高比例-zNear-zFar}//设置OenGL资源和状态;第一次调用resizeGL/paintGL调用
    void demoOpenglWidget::paintGL()
    {this->glClearColor(0.0f,0.0f,0.0f,1.0f);this->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);shaderprogram.bind();//定义参数QVector3D lightColor(1.0f,1.0f,1.0f);QVector3D objectColor(1.0f,1.0f,0.88f);//模型颜色QVector3D lightPos(0.0f,0.0f,50.0f);//传值到小程序shaderprogram.setUniformValue("objectColor",objectColor);shaderprogram.setUniformValue("lightColor",lightColor);shaderprogram.setUniformValue("lightPos", lightPos);model.setToIdentity();model.translate(xtrans,ytrans,ztrans);model.rotate(rotation);shaderprogram.setUniformValue("view",view);shaderprogram.setUniformValue("projection", projection);shaderprogram.setUniformValue("model", model);int n = vertices.capacity()/sizeof(float);qDebug() << n;//打印stl中三角形的数量(stl是多个三角形组成的文件)QOpenGLVertexArrayObject::Binder bind(&VAO);//绑定VAOthis->glDrawArrays(GL_TRIANGLES,0,n);}

2.1 读取ascll码文件

QVector<float> demoOpenglWidget::loadAscllStl(QString filename, int ratio)
{//ratio为缩放系数QVector<float> vertices_temp;qDebug()<<"load text file:"<<filename;//读取ascll码文件QFile file(filename);if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){qDebug() << "Open stl_file failed." << endl;}while(!file.atEnd()){//将文件内各个信息通过空格分隔,保存在words数组内QString line = file.readLine().trimmed();//trim去除首尾空白字符QStringList words = line.split(' ',QString::SkipEmptyParts);//法向量坐标开头为"facet"if(!file.atEnd() && words[0]=="facet"){Normal = {ratio*words[2].toFloat(),ratio*words[3].toFloat(),ratio*words[4].toFloat()};}//三角形各个顶点开头为"vertex"else if (!file.atEnd() && words[0] == "vertex") {Position = {ratio*words[1].toFloat(), ratio*words[2].toFloat(),ratio*words[3].toFloat()};vertices_temp.append(Position);vertices_temp.append(Normal);}elsecontinue;}file.close();qDebug() << "write vertice_temp success!" << filename;return vertices_temp;
}

2.2 读取二进制文件

QVector<float> demoOpenglWidget::loadBinStl(std::string filename, int ratio)
{//ratio为缩放系数QVector<float> vertices_temp;qDebug()<<"load text file:"<<filename.c_str();FILE *file = fopen(filename.c_str(),"rb");//80字节文件头char header[80];fread(header,80,1,file);//4字节三角形面片数量uint32_t triangleNum;fread(&triangleNum,sizeof(uint32_t),1,file);float n1,n2,n3;float p1,p2,p3;for(auto i=0;i<triangleNum;i++){//读取法向量信息fread(&n1,sizeof(float),1,file);fread(&n2,sizeof(float),1,file);fread(&n3,sizeof(float),1,file);qDebug()<<"n1="<<n1<<" n2="<<n2<<" n3="<<n3;Normal={n1*ratio,n2*ratio,n3*ratio};for(auto j=0;j<3;j++){//读取顶点信息fread(&p1,sizeof(float),1,file);fread(&p2,sizeof(float),1,file);fread(&p3,sizeof(float),1,file);qDebug()<<"p1="<<p1<<" p2="<<p2<<" p3="<<p3;Position = {p1*ratio,p2*ratio,p3*ratio};vertices_temp.append(Position);vertices_temp.append(Normal);}//2字节三角形面片属性char c[2];fread(c,2,1,file);}fclose(file);delete file;qDebug() << "write vertice_temp success!" << filename.c_str();return vertices_temp;
}

2.3 鼠标交互


void demoOpenglWidget::mousePressEvent(QMouseEvent *event)
{mousePos = QVector2D(event->pos());event->accept();
}void demoOpenglWidget::mouseMoveEvent(QMouseEvent *event)
{if(event->buttons() == Qt::LeftButton){QVector2D newPos = (QVector2D)event->pos();QVector2D diff = newPos - mousePos;qreal angle = (diff.length())/3.6;QVector3D rotationAxis = QVector3D(diff.y(), diff.x(), 0.0).normalized();rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angle) * rotation;mousePos = newPos;this->update();}event->accept();
}//鼠标滚轮缩放模型void demoOpenglWidget::wheelEvent(QWheelEvent *event)
{QPoint numDegrees = event->angleDelta()/8;if(numDegrees.y() > 0){ztrans += 0.25f;}else if(numDegrees.y() < 0){ztrans -= 0.25f;}this->update();event->accept();
}

2.4 cpp源代码

#include "demoopenglwidget.h"
#include"QDebug"
#include<QStringList>
#include <QFile>
#include<QtMath>demoOpenglWidget::demoOpenglWidget(QWidget *parent): QOpenGLWidget(parent),VBO(QOpenGLBuffer::VertexBuffer),xtrans(0),ytrans(0),ztrans(0.0)
//      verticesCnt(0)//顶点坐标计数
{QSurfaceFormat format;format.setAlphaBufferSize(24);format.setVersion(3,3);format.setSamples(10);//设置重采样次数,用于反走样this->setFormat(format);//导入模型文件(路径根据自己情况更改)
//    vertices=loadAscllStl(":/res/stl/demo_ascll.txt",1);vertices=loadBinStl(":/res/stl/demo_bin.stl",1);
}demoOpenglWidget::~demoOpenglWidget()
{makeCurrent();
}//渲染OpenGL场景,widget需要更新时调用
void demoOpenglWidget::initializeGL()
{this->initializeOpenGLFunctions();shaderprogram.create();if(!shaderprogram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/stl.vert"))qDebug()<<"ERROR:"<<shaderprogram.log();if(!shaderprogram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/stl.frag"))qDebug()<<"ERROR:"<<shaderprogram.log();    //如果编译出错,打印报错信息//将添加到此程序的着色器与addshader链接在一起if(!shaderprogram.link())qDebug()<<"ERROR:"<<shaderprogram.log();    //如果链接出错,打印报错信息VAO.create();VAO.bind();VBO.create();VBO.bind();VBO.allocate(vertices.data(),sizeof(float)*vertices.size());shaderprogram.setAttributeBuffer("aPos",GL_FLOAT,0,3,sizeof(GLfloat)*6);shaderprogram.enableAttributeArray("aPos");shaderprogram.setAttributeBuffer("aNormal", GL_FLOAT,sizeof(GLfloat) * 3, 3, sizeof(GLfloat) * 6);shaderprogram.enableAttributeArray("aNormal");this->glEnable(GL_DEPTH_TEST);view.setToIdentity();view.lookAt(QVector3D(0.0f,0.0f,3.0f),QVector3D(0.0f,0.0f,0.0f),QVector3D(0.0f,1.0f,0.0f));}//设置OpenGL视口、投影等。
void demoOpenglWidget::resizeGL(int w, int h)
{this->glViewport(0,0,w,h);projection.setToIdentity();projection.perspective(60.0f,(GLfloat)w/(GLfloat)h, 0.001f, 100.0f);//视角-宽高比例-zNear-zFar}//设置OenGL资源和状态;第一次调用resizeGL/paintGL调用
void demoOpenglWidget::paintGL()
{this->glClearColor(0.0f,0.0f,0.0f,1.0f);this->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);shaderprogram.bind();//定义参数QVector3D lightColor(1.0f,1.0f,1.0f);QVector3D objectColor(1.0f,1.0f,0.88f);//模型颜色QVector3D lightPos(0.0f,0.0f,50.0f);//传值到小程序shaderprogram.setUniformValue("objectColor",objectColor);shaderprogram.setUniformValue("lightColor",lightColor);shaderprogram.setUniformValue("lightPos", lightPos);model.setToIdentity();model.translate(xtrans,ytrans,ztrans);model.rotate(rotation);shaderprogram.setUniformValue("view",view);shaderprogram.setUniformValue("projection", projection);shaderprogram.setUniformValue("model", model);int n = vertices.capacity()/sizeof(float);qDebug() << n;//打印stl中三角形的数量(stl是多个三角形组成的文件)QOpenGLVertexArrayObject::Binder bind(&VAO);//绑定VAOthis->glDrawArrays(GL_TRIANGLES,0,n);}QVector<float> demoOpenglWidget::loadAscllStl(QString filename, int ratio)
{QVector<float> vertices_temp;qDebug()<<"load text file:"<<filename;QFile file(filename);if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){qDebug() << "Open stl_file failed." << endl;}while(!file.atEnd()){QString line = file.readLine().trimmed();//trim去除首尾空白字符QStringList words = line.split(' ',QString::SkipEmptyParts);if(!file.atEnd() && words[0]=="facet"){Normal = {ratio*words[2].toFloat(),ratio*words[3].toFloat(),ratio*words[4].toFloat()};}else if (!file.atEnd() && words[0] == "vertex") {Position = {ratio*words[1].toFloat(), ratio*words[2].toFloat(),ratio*words[3].toFloat()};vertices_temp.append(Position);vertices_temp.append(Normal);}elsecontinue;}file.close();qDebug() << "write vertice_temp success!" << filename;return vertices_temp;
}QVector<float> demoOpenglWidget::loadBinStl(std::string filename, int ratio)
{QVector<float> vertices_temp;qDebug()<<"load text file:"<<filename.c_str();FILE *file = fopen(filename.c_str(),"rb");//80字节文件头char header[80];fread(header,80,1,file);//4字节三角形面片数量uint32_t triangleNum;fread(&triangleNum,sizeof(uint32_t),1,file);float n1,n2,n3;float p1,p2,p3;for(auto i=0;i<triangleNum;i++){//读取法向量信息fread(&n1,sizeof(float),1,file);fread(&n2,sizeof(float),1,file);fread(&n3,sizeof(float),1,file);qDebug()<<"n1="<<n1<<" n2="<<n2<<" n3="<<n3;Normal={n1*ratio,n2*ratio,n3*ratio};for(auto j=0;j<3;j++){//读取顶点信息fread(&p1,sizeof(float),1,file);fread(&p2,sizeof(float),1,file);fread(&p3,sizeof(float),1,file);qDebug()<<"p1="<<p1<<" p2="<<p2<<" p3="<<p3;Position = {p1*ratio,p2*ratio,p3*ratio};vertices_temp.append(Position);vertices_temp.append(Normal);}//2字节三角形面片属性char c[2];fread(c,2,1,file);}fclose(file);delete file;qDebug() << "write vertice_temp success!" << filename.c_str();return vertices_temp;
}void demoOpenglWidget::mousePressEvent(QMouseEvent *event)
{mousePos = QVector2D(event->pos());event->accept();
}void demoOpenglWidget::mouseMoveEvent(QMouseEvent *event)
{if(event->buttons() == Qt::LeftButton){QVector2D newPos = (QVector2D)event->pos();QVector2D diff = newPos - mousePos;qreal angle = (diff.length())/3.6;QVector3D rotationAxis = QVector3D(diff.y(), diff.x(), 0.0).normalized();rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angle) * rotation;mousePos = newPos;this->update();}event->accept();
}void demoOpenglWidget::wheelEvent(QWheelEvent *event)//鼠标滚轮缩放模型
{QPoint numDegrees = event->angleDelta()/8;if(numDegrees.y() > 0){ztrans += 0.25f;}else if(numDegrees.y() < 0){ztrans -= 0.25f;}this->update();event->accept();
}

3.问题

3.1 内存溢出

out of memory 内存溢出 在pro配置文件加入CONFIG+=resources_big

3.2 三角形面片过多

可以发现在解析bin的文件时,默认四字节的三角形面片数,也即超过0xFFFF(65535)个面片将会内存溢出。当在解析函数中更改为64位读取时发现也不成功,仍然出现out of memory内存溢出错误;

解决方案:

配置文件加CONFIG+=resources_big

采取精简面片,此时面片为0xfe0e刚好在32位内,但此时发现文件读取不了,fopen一直打开是空指针。

查了很多博客发现还是无法解决,突然想到之前打开ascll码文件时出现文件内存溢出,再试一下ascll码打开文件,成功。

  1. 选择物体,打开修改器,选择精简
  2. 修改塌陷比率,越低三角形面片越少,当然模型越粗糙。
  3. 可以看出此时模型三角形清晰可见,面片变少,当然模型非常粗糙。

这样做的目的是:

  • ascll码&&二进制文件过大,加载较慢甚至加载不出来,故此步骤相当于压缩

  • 当面片数过多时,在读取ascll码文件时,在提取完80个字节的头文件信息后,发现三角形面片数量保存在32位4字节中,当面片数量超出0xffff时,猜测面片数量将会以64位存储(甚至更大),但是具体怎么处理我还没有实现。to be continue....

4.总结

模型处理时:

- 坐标中心位于模型中心,方便绕着模型中心旋转
- 面片过大时在blender中修改精简比例
- 输出文件注意是ascll码格式还是二进制文件

工程目录中:

- 内存溢出时加入配置文件:CONFIG+=resources_big
- 目录注意更改(ascll可用相对路径,二进制用绝对(不要出现中文))

5.参考博客

QT OpenGL加载STL模型文件并旋转放缩_GT_L_0813的博客-CSDN博客

Qt/C++ + opengl 解析stl文件(二进制和Ascii两种格式)_shenmingyi.blog.csdn.net-CSDN博客_qt stl文件

6.工程文件

OpenGL_Qt: 实现加载ascll码&&二进制格式stl文件加载,并在OpenGL控件中展示。实现了鼠标拖动旋转,滚轮缩放。注意ascll可用相对路径加载,二进制仅可用绝对路径加载。值得注意的是二进制文件无法打开过大文件(打开文件指针为空),此项目只适用于较小stl文件读取。

QT+OpenGL导入STL文件(二进制/ascll码格式),鼠标交互实现缩放旋转相关推荐

  1. Qt/C++ + opengl 解析stl文件(二进制和Ascii两种格式)

    前言: 3D 的stl 模型文件分为两种 二进制和Ascii 明码的Ascii 内容清晰可以打开看,但是文件比较大 二进制的文件 看不到内容 但是占用空间小 我是用 qt + opengl 加载 st ...

  2. stl文件用proe怎么打开_3dMax怎么导入proe的文件渲染?3dmax导入stl文件过程

    proe自带的渲染器渲染效果比较差,一般我们都是导出文件到3dsMax或者其它专业的渲染器来渲染更逼真的效果.不过3Dmax是不能直接打开stp文件的,打开igs的文件经常有很多破面.3Dmax怎么导 ...

  3. 3D Slicer 中导入STL文件

    3D Slicer 中导入STL文件,模型不显示 两种方法: 1.点击"DATA" 获取 STL 文件路径 2. 直接将STL 文件拖拽到视图中. 文件不显示的原因是STL文件的路 ...

  4. stl文件用proe怎么打开_3dmax怎么导入stl文件?如何将proe的文件导入3dmax进行渲染?...

    我们都知道3dmax是无法直接打开stp文件的,因为proe自带的渲染器渲染效果比较差.所以很多人想将proe文件导入3dmax来渲染,那么怎么导入proe文件到3dmax呢?接下来就来揭晓答案. 1 ...

  5. Qt 如导入SQL文件

    Qt 可以做什么? Qt 虽然经常被当做一个 GUI 库,用来开发图形界面应用程序,但这并不是 Qt 的全部:Qt 除了可以绘制漂亮的界面(包括控件.布局.交互),还包含很多其它功能,比如多线程.访问 ...

  6. OpenGL学习笔记3 —— 绘制3D物体、鼠标交互、反向变换

    /* reference http://nehe.gamedev.net/article/using_gluunproject/16013/ */#include <windows.h> ...

  7. [OpenGL]导入obj文件

    通常来说我们构建一个模型是比较复杂的工作,那么我们还有什么途径获得模型呢.其中一种方法就是导入obj模型,不要被这个名词给吓到了,其实就是把一个制作好的模型保存到一个文件中,我们称为obj文件. 先上 ...

  8. Qt|OpenGL对obj文件的认识以及如何去解析(opengl+qt构造地球模型)

    首先来个简单点的.obj文件(如下图所示): 总体处理伪代码如下: void GraphicsModel::init(const QString filePath) {QFile file(fileP ...

  9. mysql 导入tsv文件,MySQL导出TSV格式文件

    可以使用mysqldump, 也可以使用mysql -e 使用mysqldump 因为要使用到 -T / --tab 参数, 需要先查看mysql设置的secure_file_priv mysql&g ...

最新文章

  1. 九种浏览器端缓存机制知多少(转)
  2. 找到一个全能的免费空间!支持SQL和.net2.0
  3. vue 虚拟服务器,vue+webpack项目中使用dev-server搭建虚拟服务器,请求json文件数据,实现先后台分离开发...
  4. 合辑 | 面试必备!18篇Java面试疑难点详解
  5. .NET MAUI 预览版 6 发布
  6. code block怎样实现图形界面_微服务入门:Openresty实现API网关
  7. 《C和指针》——将无符号整数转换为字符
  8. 把区块链作为核心技术自主创新重要突破口:华为云可以做什么?【华为云分享】
  9. IllegalStateException: Error reading delta file hdfs://xxx/spark/xx/state/0/11/1.delta
  10. [轉]Windows下Subversion配置管理员指南
  11. Linux系统修改编码(转)
  12. 机器学习中为什么需要梯度下降_机器学习 —— 多元梯度下降
  13. 飞翔pdf生成html,pdf转换成html转换器
  14. 用HTML来做导航栏
  15. 美国计算机专业工资排名,美国大学薪酬概况及毕业生薪资排名前50的学校
  16. uniapp H5 调用高德地图导航
  17. 基于多进程架构的嵌入式软件框架研究与实现
  18. 将MP4文件在网页端播放
  19. Spring Boot整合MyBatis框架(XML文件版)
  20. java小程序-画一幅海底世界的图

热门文章

  1. 有些人之所以不断成长,就绝对是有一种坚持下去的力量。
  2. js给input控件添加onkeypress属性
  3. 副业怎么赚到钱?有哪些能赚钱的副业?
  4. 你不知道的“探探”引流套路,让更多人“喜欢你”!
  5. 我给舅舅用Python写了一个订餐系统微信小程序!生意简直火爆!
  6. PPT录屏怎么把自己录进去?只需要这样做!
  7. 区块链手机”得实现这4大功能
  8. 【JVM技术专题】「源码专题」深入剖析JVM的Mutex锁的运行原理及源码实现(底层原理-防面试)
  9. 牛客寒假6-C.项链
  10. c语言连连看算法,C++实现连连看消除算法