基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(十五)obj模型加载

2023-11-14

Vries的原教程里,对于模型载入,使用的是一种非常流行的模型加载库Assimp,可以方便的加载obj,fbx,3ds等常见的模型格式文件,在visual studio2015里,我照原教程进行了Assimp的配置,程序成功运行。在Qt中,把Assimp当作外库进行导入,试了很多种方法也不可以,万般无奈之下,我自写了一个基于Qt平台的简易模型导入程序,仅针对简易obj模型进行解析导入。

https://learnopengl-cn.github.io/03%20Model%20Loading/01%20Assimp/关于Assimp库的内容与常见模型只是请看Vries的原版教程)

 

Qt开发平台:5.8.0

编译器:Desktop Qt 5.8.0 MSVC2015_64bit

 

本程序源代码

百度网盘链接:https://pan.baidu.com/s/1w60NPe69ySSqzkQ6o0BeVA 密码:dmud

csdn下载连接https://download.csdn.net/download/z136411501/10611540

一,程序截图

1.1 开启光照
1.2 关闭光照
1.2 关闭光照
1.3 线性渲染

 

1.4 比武台
1.5 坦克
1.6 基地

二,obj模型解释

  这里借用一下另外一个学习可编程管线OpenGL的网站里,对obj模型的解释,网站原链接如下:

http://www.opengl-tutorial.org/cn/beginners-tutorials/tutorial-7-model-loading/

 相对于原链接所介绍的简易OBJ模型,我增添了一些内容,主要是材质文件mtllib的使用,

 

  2.1 OBJ文件示例

  在原作者的示例obj文件中,增添材质属性的使用,大概是这个模样:

# Blender3D v249 OBJ File: untitled.blend
# www.blender3d.org

mtllib cube.mtl

#
# object Arch41_039
#

v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.748573 0.750412
vt 0.749279 0.501284
vt 0.999110 0.501077
vt 0.999455 0.750380
vt 0.250471 0.500702
vt 0.249682 0.749677
vt 0.001085 0.750380
vt 0.001517 0.499994
vt 0.499422 0.500239
vt 0.500149 0.750166
vt 0.748355 0.998230
vt 0.500193 0.998728
vt 0.498993 0.250415
vt 0.748953 0.250920
vn 0.000000 0.000000 -1.000000
vn -1.000000 -0.000000 -0.000000
vn -0.000000 -0.000000 1.000000
vn -0.000001 0.000000 1.000000
vn 1.000000 -0.000000 0.000000
vn 1.000000 0.000000 0.000001
vn 0.000000 1.000000 -0.000000
vn -0.000000 -1.000000 0.000000
usemtl Material_ray

s off
f 5/1/1 1/2/1 4/3/1
f 5/1/1 4/3/1 8/4/1
f 3/5/2 7/6/2 8/7/2
f 3/5/2 8/7/2 4/8/2
f 2/9/3 6/10/3 3/5/3
f 6/10/4 7/6/4 3/5/4
f 1/2/5 5/1/5 2/9/5
f 5/1/6 6/10/6 2/9/6
f 5/1/7 8/11/7 6/10/7
f 8/11/7 7/12/7 6/10/7
f 1/2/8 2/9/8 3/13/8
f 1/2/8 3/13/8 4/14/8
  • #是注释标记,就像C++中的//
  • object是将一个obj模型分为多个模块,进行储存管理
  • mtllib描述了模型所使用的材质文件所在的路径,材质文件里一般会有多个材质
  • usemtl表示接下来的面f所构成的三维几何结构的材质属性使用该种材质
  • v顶点
  • vt代表顶点的纹理坐标
  • vn代表顶点的法线
  • f代表面

   v vt vn都很好理解。f比较麻烦。例如f 8/11/7 7/12/7 6/10/7:

  • 8/11/7描述了三角形的第一个顶点
  • 7/12/7描述了三角形的第二个顶点
  • 6/10/7描述了三角形的第三个顶点
  • 对于第一个顶点,8指向要用的顶点。此例中是-1.000000 1.000000 -1.000000(索引从1开始,和C++中从0开始不同)
  • 11指向要用的纹理坐标。此例中是0.748355 0.998230。
  • 7指向要用的法线。此例中是0.000000 1.000000 -0.000000。

我们称这些数字为索引。若几个顶点共用同一个坐标,索引就显得很方便,文件中只需保存一个”v”,可以多次引用,节省了存储空间。

以下是原教程一个简易的obj载入函数,仅一个函数即可完成模型载入的关键步骤,不过读取的模型不包含材质文件,只有最基础的顶点位置参数,且读取文件时使用的是最基础的stdio.h标准输入输出流。

百度网盘链接:https://pan.baidu.com/s/15aQ3QbHtzzkt4zQXE6oF3g 密码:22el

 

  2.2 mtl文件示例

   这是一个简单的mtl材质文件:

# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# 创建的文件:01.03.2017 19:24:15

newmtl 01___Default
	Ns 58.0000
	Ni 1.5000
	d 1.0000
	Tr 0.0000
	Tf 1.0000 1.0000 1.0000 
	illum 2
	Ka 1.0000 1.0000 1.0000
	Kd 1.0000 1.0000 1.0000
	Ks 0.1167 0.1167 0.1167
	Ke 0.0000 0.0000 0.0000
	map_Ka white.jpg
	map_Kd white.jpg

newmtl 17___Default
	Ns 1.0000
	Ni 1.5000
	d 1.0000
	Tr 0.0000
	Tf 1.0000 1.0000 1.0000 
	illum 2
	Ka 0.5882 0.5882 0.5882
	Kd 0.5882 0.5882 0.5882
	Ks 0.0000 0.0000 0.0000
	Ke 0.0000 0.0000 0.0000
	map_Ka Arch41_039_bark.jpg
	map_Kd Arch41_039_bark.jpg
  • newmtl代表一种材质,以下皆为该材质的属性参数
  • Ns为Phong式光照模型中镜面光的高光反射系数
  • Ka为Phong式光照模型中环境光的颜色反射系数
  • Kd为Phong式光照模型中漫反射光的颜色反射系数
  • Ks为Phong式光照模型中镜面光的颜色反射系数
  • map_Ka为环境光所采样的纹理贴图路径,在.obj模型文件的根目录下
  • map_Kd为漫反射光所采样的纹理贴图路径
  • 其余参数感兴趣的大家自己查呗,反正我没用上

三,源代码解析

  3.1 项目目录

      

    相对于教程(八)的简单框架,我进行了进一步的精简

  • camera.cpp是摄像机文件,使用WASDEQ按键控制摄像机的前进后退上升下降,鼠标左键拖拽进行视角的移动
  • light.cpp是灯光,存储一个简单的6面,36个顶点的立方体
  • main.cpp主函数调用主程序接口,与使用qss样式文件
  • mainwindow.cpp打开主窗口,相应按键与按钮
  • model.cpp模型文件类,读取指定路径下的obj模型文件
  • oglmanager.cpp继承QOpenGLWidget类,作为附属于窗口类的widget类使用
  • resourcemanager.cpp作为资源管理类,管理shader与texture纹理资源
  • shader.cpp本质对象为QOpenGLShader,做了一些方便管理参数的成员函数
  • texture2d.cpp本质对象为QOpenGLTexture,设置成员函数方便管理

  3.2 模型读取函数解析

这里仅解释model.hmodel.cpp这个类,其余.h与.cpp文件,之前教程有过解析。

model.h

#ifndef MODEL_H
#define MODEL_H

#include <QOpenGLFunctions_3_3_Core>
#include <resourcemanager.h>
#include <QVector>

#include <QDebug>
#include <QVector3D>
#include <QVector2D>
#include <QOpenGLTexture>
#include <QMap>

class Object;
class Material;

class Model
{
public:
  Model();
  bool init(const QString& path); //模型文件的初始化设置,将.obj文件的路径传入参数列表
  void draw(GLboolean isOpenLighting = GL_FALSE);//绘制模型,参数列表为是否打开Phong式光照计算
private:
  bool loadOBJ(const QString& path);//整个程序的最关键函数!!! 参数为obj模型所在的路径
  void bindBufferData();
  QOpenGLFunctions_3_3_Core *core;
  QVector<Object> objects; //存储模型中的各个object模块
  QMap<QString, Material> map_materials;//读取该模型的材质文件,并存储文件中的各个材质属性

};

class Object{
public:
  GLuint positionVBO;
  GLuint uvVBO;
  GLuint normalVBO;

  QVector<QVector3D> positions;
  QVector<QVector2D> uvs;
  QVector<QVector3D> normals;

  QString matName;//材质名称
};

class Material{//一个简易的材质类
public:
  QVector3D Ka;//ambient反射系数
  QVector3D Kd;//diffuse反射系数
  QVector3D Ks;//specular反射系数
  double shininess;
  QString name_map_Ka;
  QString name_map_Kd;

};

#endif // MODEL_H

  我借鉴了Assimp文件库的模型读取思想,将obj模型含有一个或多个object对象,一个object对象含有多个顶点,法向量,纹理坐标与材质信息,一个材质含有Phong式光照模型中的环境光,漫反射光,镜面光反射系数,镜面反射指数,环境纹理贴图与漫反射纹理贴图路径。

解释一下关键函数loadOBJ(const QString &path),流程就是使用QFile打开.obj模型文件所在的路径,while循环,一步一步扫描整个文件,遇到关键字,如object,usemtl,v,或者vn,等分开进行处理,处理完后,绑定数据运行即可。

bool Model::loadOBJ(const QString &path){
  QFile file(path);
  if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    qDebug()<<"OBJLOADER ERROR::FILE CAN NOT OPEN!";
    file.close();
    return false;
  }

  QTextStream in(&file);
  QString line;//文件流

  QVector<int> positionIndices, uvIndices, normalIndices;
  QVector<QVector3D> temp_positions;
  QVector<QVector2D> temp_uvs;
  QVector<QVector3D> temp_normals;
  QString temp_matName;//材质的名称

  while(!in.atEnd()){
    line = in.readLine();//读取一行,还有读取所有readAll();
    QStringList list = line.split(" ", QString::SkipEmptyParts);
    if(list.empty())
      continue;
    //qDebug() << list;
    if(list[0] == "mtllib"){//处理材质文件,即处理图片纹理
      /******* 1.1 处理材质文件路径 *********/
      //":/models/res/models/huapen/penzi.obj"
      QString mtl_path = path;
      int tempIndex = path.lastIndexOf("/")+1;
      mtl_path.remove(tempIndex, path.size()-tempIndex);//":/models/res/models/huapen/" 得到根目录路径,用来和材质文件名结合,生成正确路径
      //mtl_path += list[1];//得到材料路径":/models/res/models/huapen/penzi.mtl"
//      qDebug() << mtl_path;

      /******* 1.2 读取材质文件,导入Material类中 *********/
      QFile mtl_file(mtl_path+list[1]);//正确的材质文件路径
      if(!mtl_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug()<<"OBJLOADER ERROR::MTL_FILE CAN NOT OPEN!";
        mtl_file.close();
        file.close();
        return false;
      }
      QTextStream mtl_in(&mtl_file);
      QString mtl_line;//读取材质文件流的一行

      Material material;
      QString matName;//材质的名称
      while(!mtl_in.atEnd()){
        mtl_line = mtl_in.readLine();//读取一行,还有读取所有readAll();
        QStringList mtl_list = mtl_line.split(QRegExp("\\s+"), QString::SkipEmptyParts); //以“空格”与“\t”为识别符号,分开字符串
        if(mtl_list.empty())
          continue;
        if(mtl_list[0] == "newmtl"){
          matName = mtl_list[1];
          map_materials[matName] = material;
        }else if(mtl_list[0] == "Ns"){
          double shininess = mtl_list[1].toDouble();
          map_materials[matName].shininess = shininess;
        }else if(mtl_list[0] == "Ka"){
          double x = mtl_list[1].toDouble();
          double y = mtl_list[2].toDouble();
          double z = mtl_list[3].toDouble();

          QVector3D Ka(x, y, z);
          map_materials[matName].Ka = Ka;
        }else if(mtl_list[0] == "Kd"){
          double x = mtl_list[1].toDouble();
          double y = mtl_list[2].toDouble();
          double z = mtl_list[3].toDouble();

          QVector3D Kd(x, y, z);
          map_materials[matName].Kd = Kd;
        }else if(mtl_list[0] == "Ks"){
          double x = mtl_list[1].toDouble();
          double y = mtl_list[2].toDouble();
          double z = mtl_list[3].toDouble();

          QVector3D Ks(x, y, z);
          map_materials[matName].Ks = Ks;
        }else if(mtl_list[0] == "map_Ka"){
          ResourceManager::loadTexture(mtl_list[1], mtl_path+mtl_list[1]);
          map_materials[matName].name_map_Ka = mtl_list[1];
        }else if(mtl_list[0] == "map_Kd"){
          ResourceManager::loadTexture(mtl_list[1], mtl_path+mtl_list[1]);
          map_materials[matName].name_map_Kd = mtl_list[1];
        }
      }
     /******* 1.2 读取材质文件,导入Material类中 *********/
    }else if(list.size() > 1 && list[1] == "object"){//扫描寻找object
      if(!objects.empty()){
        for(int i=0; i < positionIndices.size(); i++ ){
          //得到索引
          int posIndex = positionIndices[i];
          int uvIndex = uvIndices[i];
          int norIndex = normalIndices[i];

          //根据索引取值
          QVector3D pos = temp_positions[posIndex-1];
          objects.last().positions.push_back(pos);

          QVector3D nor = temp_normals[norIndex-1];
          objects.last().normals.push_back(nor);

          if(uvIndex != 0){
            QVector2D uv = temp_uvs[uvIndex-1];
            objects.last().uvs.push_back(uv);
          }

        }
        objects.last().matName = temp_matName;
        positionIndices.clear();
        uvIndices.clear();
        normalIndices.clear();
      }

      Object object;
      objects.push_back(object);//obj模型文件中的第一个object对象,因为一个obj模型可能还有多个object对象
    }else if (list[0] == "v"){
      double x = list[1].toDouble();
      double y = list[2].toDouble();
      double z = list[3].toDouble();

      QVector3D pos;
      pos.setX(x);
      pos.setY(y);
      pos.setZ(z);
      temp_positions.push_back(pos);
    }else if (list[0] == "vt"){
      double x = list[1].toDouble();
      double y = list[2].toDouble();

      QVector2D uv;
      uv.setX(x);
      uv.setY(y);
      temp_uvs.push_back(uv);
    }else if (list[0] == "vn"){
      double x = list[1].toDouble();
      double y = list[2].toDouble();
      double z = list[3].toDouble();

      QVector3D nor;
      nor.setX(x);
      nor.setY(y);
      nor.setZ(z);
      temp_normals.push_back(nor);
    }else if (list[0] == "usemtl"){
      temp_matName = list[1];
      //qDebug() << list[1];
    }else if (list[0] == "f"){
      if(list.size() > 4){
        qDebug() << "OBJLOADER ERROR::THE LOADER ONLY SUPPORT THE TRIANGLES MESH!" << endl;
        file.close();
        return false;
      }
      for(int i = 1; i < 4; ++i){//读取处理 f字符后边的 三长串字符,如“f 2396/2442/2376 101/107/111 100/106/110”
        QStringList slist = list[i].split("/");
        int posIndex = slist[0].toInt();
        int uvIndex = slist[1].toInt();
        int norIndex = slist[2].toInt();

        positionIndices.push_back(posIndex);
        uvIndices.push_back(uvIndex);
        normalIndices.push_back(norIndex);
        //qDebug() <<posIndex << " " << uvIndex << " " << norIndex;
      }
    }
  }

  //处理最后一个object

  for(int i=0; i < positionIndices.size(); i++ ){
    //得到索引
    int posIndex = positionIndices[i];
    int uvIndex = uvIndices[i];
    int norIndex = normalIndices[i];

    //根据索引取值
    QVector3D pos = temp_positions[posIndex-1];
    objects.last().positions.push_back(pos);

    QVector3D nor = temp_normals[norIndex-1];
    objects.last().normals.push_back(nor);

    if(uvIndex != 0){
      QVector2D uv = temp_uvs[uvIndex-1];
      objects.last().uvs.push_back(uv);
    }
            //qDebug() <<posIndex << " " << uvIndex << " " << norIndex;
  }
  objects.last().matName = temp_matName;

  file.close();
  return true;
}

四,注意事项

      因为这是一个简易的obj模型读取程序,一些细节处,我懒得处理,所以如果要载入一些新的obj模型,务必修改文件

  的格式,使之与penzi.obj与penzi.mtl的格式对应。

        

 

比如,

  • 该读取格式,读取obj模型必须有与之对应的mtl材质文件存在
  • penzi.obj里材质文件的路径必须为相对路径
  • penzi.obj必须是纯三角形面,不能有四边形等多边形面
  • 每一个面 f 11/2/3,顶点,法线,纹理坐标均需存在,缺一不可
  • penzi.mtl里必须有纹理贴图的路径存在

     另外,该Model类对象,不能拿出来直接单独使用,必须结合指定着色器“model”与ResourceManagersh类使用(资源文件中有,本来想把Model类写成一个独立的类来使用,想了半天,处理起来太麻烦了,没有好的思路,就算了)

看着该读取程序有很多限制,其实改起来也特别简单,学个思想就行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(十五)obj模型加载 的相关文章

  • cURL简单使用、libcurl编程, curl_easy_setopt

    更多关于curl的信息可以参考官网http curl haxx se http curl haxx se libcurl c curl easy setopt html CURLcodecurl easy setopt CURL handl
  • Java-集合(LinkedList类)

    简单介绍 1 LinkedList底层实现了双向链表和双端队列特点 2 可以添加任意元素 元素可重复 包括null 3 线程不安全 没有实现同步 底层操作机制 1 LinkedList底层维护了一个双向链表 2 LinkedList中维护了
  • java字符串判空失败

    那你就快放弃使用 equals lkm 或 lkm isEmpty 进行判空吧 最牛逼判空方法 StringUtils isEmpty lkm 判空失败还有一种可能 基本用什么方法判空都会失败 使用String value 进行转换的判空
  • Python调用ImageMagick生成PDF文件缩略图

    使用Python调用ImageMagick生成PDF文件缩略图 Imagemagick使用Ghostscript作为其依赖项之一 以便能够处理和转换PDF相关的图像 准备 安装Ghostscript 网站 安装ImageMagick 网站
  • 计算机上开发和运行应用程序与操作系统无关,计算机期中测验理论部分和答案-C.doc...

    计算机期中测验理论部分和答案 C 一 判断题 共10分 正确填写T 反之填写F 1 Cache和主存储器虽然都是用半导体芯片作为存储介质 但前者使用SRAM 后者使用DRAM 前者比后者的存取速度快 正确答案 T 2 当一个应用程序窗口被最
  • Spring中比较常用的工具类

    清单 1 BeanDefinitionReaderUtils registerBeanDefinition 往spring容器注册一个beanDefinition 2 PropertiesLoaderUtils loadAllPropert
  • timm库(CV利器)的入门教程(1)

    省流 使用timm加载CNN进行图像分类 调整CNN使之更适合你的任务 问 使用timm搭建一个可以使用的CNN或ViT拢共需要几步 答 4步 0 安装 timm 1 import timm 2 创建model 3 运行model 这一节很

随机推荐

  • 二叉搜索树(树状数组)

    计数函数 程序 int lowbit int k return k k 功能 可视为每个节点的编号函数 加和函数 程序 int sum int x int ret 0 while x gt 0 ret c x x lowbit x retu
  • jmeter提取token后只能使用一次的鬼问题

    今天在测试一个内部接口时 需要先登录传入token 按照正常步骤作对应参数化和参数传递 过程略 提取token的方法有很多 这里我是用的是正则表达式 提了就正常用嘛 可发现不管下边添加了多少请求 同一线程组下 永远只有登录请求的下一个可以获
  • linux常用函数和知识点博文合集

    signal SIGCHLD SIG IGN 和signal SIGPIPE SIG IGN 中断产生EINTR错误 linux中sigaction函数详解 标准I O函数库提供了popen函数 它启动另外一个进程去执行一个shell命令行
  • 2023秋招算法题每日学习(4)

    DAY 4 1 AcWing 850 Dijkstra求最短路 ii 考察点 堆优化Dijkstra 求最短路问题 适合于稀疏图 利用邻接表来存储 邻接表不需要对重边做特殊的处理 1 基础知识 时间复杂度分析 堆优化Dijkstra 堆优化
  • JUST技术:利用迁移学习生成新城市的轨迹

    市民的出行轨迹数据无论是对于城市管理 规划 还是商业活动 都是重要的参考信息 然而 获取一个城市的人群轨迹数据却非常困难 在今年4月份召开的国际顶级互联网会议WWW 2020 CCF A类 上 京东城市报告了被会议收录的论文 What is
  • 解决安装强化学习库gymnasium,box2d安装报错的问题

    gymnasium是强化学习的库 比较难安装 一 安装方法 安装Gymnasium 一定要all 这样可以安装所有依赖 pip install gymnasium all pip install gymnasium all 二 报错信息一
  • Docker安装记录

    1 卸载旧版本 yum remove docker docker client docker client latest docker common docker latest docker latest logrotate docker
  • Git版本控制

    Git 版本控制 什么是版本控制 版本控制是一种开发的过程中用于管理我们对文件 目录或工程等内容的修改历史 方便查看更改历史记录 备份以便恢复以前的版本的软件工程技术 实现跨区域多人协同开发 追踪和机载一个或者多个文件的历史记录 组织和保护
  • 成功解决VMware安装操作系统出现分辨率的问题

    文章目录 问题重现 问题原因 问题解决 方法一 拓展 1 电脑分辨率 2 xrandr命令 3 查询后如果没有合适的分辨率解决方案 参考资料 问题重现 如下图 在VMware16上安装ubuntu操作系统的时候 出现分辨率问题 导致底部的按
  • < 面试知识点:什么是 Node.js ?有哪些优缺点?应用场景? >

    文章目录 一 什么是 Node js 二 Node js 有哪些优缺点 三 Node js 能做什么 四 Node js 的 模块系统 五 Node js 应用场景 参考文献 往期内容 一 什么是 Node js Node js 是一个开源
  • 微信小程序如何获取微信昵称和头像

  • MySQL 高可用:mysql+mycat实现数据库分片(分库分表)

    什么是MYCAT 一个彻底开源的 面向企业应用开发的大数据库集群 支持事务 ACID 可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库 用来替代昂贵的Oracle集群 一个融合内存缓存技术 NoSQL技术 HDFS
  • Docker入门命令详解

    目录 一 Docker 简介 1 Docker引擎 2 Docker客户端 3 Docker镜像 4 Docker容器 5 Docker镜像注册中心 二 Docker的特点 1 快速运行 2 节省资源 3 便于交付 4 容易管理 三 Doc
  • C语言有小数乘法,十道小数乘小数的乘法竖式计算并有答案

    十道小数乘小数的乘法竖式计算并有答案以下文字资料是由 历史新知网www lishixinzhi com 小编为大家搜集整理后发布的内容 让我们赶快一起来看一下吧 十道小数乘小数的乘法竖式计算并有答案 6 5 8 4 56 1 3 3 2 2
  • C#实现串口通信

    背景 在工业控制中 工控机 一般都基于Windows平台 经常需要与智能仪表通过串口进行通信 串口通信方便易行 应用广泛 一般情况下 工控机和各智能仪表通过RS485总线进行通信 RS485的通信方式是半双工的 在Win32下 可以使用两种
  • 04_两种常见的网页反爬措施及应对方法

    一 封禁IP地址反爬 1 应对思路 理解这种反爬方法的含义 当我们用自己电脑的ip地址短时间 高频率访问某个具有此类反爬设置的网站 这种网站就会把我们的ip地址封禁 一般都是封24小时或者其他时间 解决方案 通过代理ip访问 这种方式只不过
  • SQL 语句 最大长度限制 DB2如何修改最大长度限制

    home datamart db2 get db cfg grep HEAP Sort heap thres for shared sorts 4KB SHEAPTHRES SHR AUTOMATIC Sort list heap 4KB
  • PhotonServer介绍

    官网 On Premises Cross Platform Multiplayer Game Backend Photon Engine Photon为您完全封装了每个客户端平台的复杂网络层 Photon Server支持可靠的UDP TC
  • 作为一个编程新手,我再也不怕Flink迷了我的眼!

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由kyledong 发表于云 社区专栏 使用 Flink 编写处理逻辑时 新手总是容易被林林总总的概念所混淆 为什么 Flink 有那么多的类型声明方式 BasicTypeInf
  • 基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(十五)obj模型加载

    Vries的原教程里 对于模型载入 使用的是一种非常流行的模型加载库Assimp 可以方便的加载obj fbx 3ds等常见的模型格式文件 在visual studio2015里 我照原教程进行了Assimp的配置 程序成功运行 在Qt中