IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Dust3D开源项目分析——渲染与材质部分 | 纹理生成 Part1 -> 正文阅读

[游戏开发]Dust3D开源项目分析——渲染与材质部分 | 纹理生成 Part1

?2021SC@SDUSC

????????通过阅读源码文件和查阅相关资料,笔者发现Dust 3D使用的图形引擎是(基于Qt的)OpenGL,或者说QOpenGL。就Qt 5.0 而言,QOpenGL是建立在OpenGL ES(Embeded System)?2.0规范上的,可以说是OpenGL ES 3.0的弱化版本。事实上OpenGL ES 2.0是2004年发布的一个相当古老的版本,并不支持部分相对先进的OpenGL方法、数据格式、快捷算法等功能。笔者认为之所以选用2.0版本,一方面是因为Dust 3D是一个跨平台的开源项目,而OpenGL ES 2.0规范基本上被所有移动平台和现代PC平台支持,因此选用QOpenGL可以在很大程度上保证Dust 3D的可移植性,为跨平台开发提供更多便利。另一方面因为Dust 3D本身目的是借助辅助建模、辅助动画等功能加速游戏开发或影视制作流程,所以对渲染方面没有太多的要求,只需要保证软件的交互式操作视窗能够流畅运行即可,即使不用高版本的OpenGL方法也可以实现。

????????由于渲染模块涉及到多个CPP源文件,笔者会结合不同文件之间的调用关系和重要性对这些文件展开分析。

代码分析

texturetype.h 和 texturetype.cpp

????????Texturetype文件中定义了Dust 3D所使用的贴图类型和一些转换函数。根据枚举类TextureType,可知这些类型包括None-空类型、?BaseColor-颜色贴图(准确来说应该是Diffuse-漫射贴图)、Normal-法线贴图、Metallic-光泽度贴图、Roughness-粗糙度贴图、Ambient Occlusion-环境遮蔽贴图。Count在后面的函数暂时没有看到被使用,推测是用于计数之类的目的。

enum class TextureType
{
    None,
    BaseColor,
    Normal,
    Metallic,
    Roughness,
    AmbientOcclusion,
    Count
};

????????头文件中有三个转换函数:QString TextureTypeToDispName(TextureType type), ?const char *TextureTypeToString(TextureType type), TextureType TextureTypeFromString(const char *typeString),用途是TextureType类型和字符串的相互转换。

????????其中TextureTypeToDispName()将TextureType类型转换成用于界面显示的字符串,调用了tr()函数,例如QObject::tr("Base Color")。tr()函数是Qt中软件国际化的核心功能,只要通过tr()传递将要显示在用户界面上的字符串,Qt就会根据用户的语言环境将字符串替换成翻译人员使用Qt Linguist做好本地化的版本。

????????其他两个函数是将材质类型字符串和对应的枚举类型相互转换,功能比较简单,这里不再赘述。

QString TextureTypeToDispName(TextureType type)                     
{                                                                   
    switch (type) {                                                 
        case TextureType::BaseColor:                                
            return QObject::tr("Base Color");                       
        case TextureType::Normal:                                   
            return QObject::tr("Normal Map");                       
        case TextureType::Metallic:                                 
            return QObject::tr("Metallic");                         
        case TextureType::Roughness:                                
            return QObject::tr("Roughness");                        
        case TextureType::AmbientOcclusion:                         
            return QObject::tr("Ambient Occlusion");                
        case TextureType::None:                                     
            return QObject::tr("None");                             
        default:                                                    
            return "";                                              
    }                                                               
}

const char *TextureTypeToString(TextureType type);
#define IMPL_TextureTypeToString                                    
const char *TextureTypeToString(TextureType type)                   
{                                                                   
    switch (type) {                                                 
        case TextureType::BaseColor:                                
            return "BaseColor";                                     
        case TextureType::Normal:                                   
            return "Normal";                                        
        case TextureType::Metallic:                                 
            return "Metallic";                                      
        case TextureType::Roughness:                                
            return "Roughness";                                     
        case TextureType::AmbientOcclusion:                         
            return "AmbientOcclusion";                              
        case TextureType::None:                                     
            return "None";                                          
        default:                                                    
            return "";                                              
    }                                                               
}
TextureType TextureTypeFromString(const char *typeString);
#define IMPL_TextureTypeFromString                                  
TextureType TextureTypeFromString(const char *typeString)           
{                                                                   
    QString type = typeString;                                      
    if (type == "BaseColor")                                        
        return TextureType::BaseColor;                              
    if (type == "Normal")                                           
        return TextureType::Normal;                                 
    if (type == "Metallic")                                         
        return TextureType::Metallic;                               
    if (type == "Metalness")                                        
        return TextureType::Metallic;                               
    if (type == "Roughness")                                        
        return TextureType::Roughness;                              
    if (type == "AmbientOcclusion")                                 
        return TextureType::AmbientOcclusion;                       
    return TextureType::None;                                       
}
#endif

?

texturegenerator.h 和 texturegenerator.cpp

????????这两个文件是实现添加纹理这一功能的核心部分。texturegenerator.h对纹理生成器类做了定义。

class TextureGenerator : public QObject
{
    Q_OBJECT
public:
    TextureGenerator(const Object &object, Snapshot *snapshot=nullptr);
    ~TextureGenerator();
    QImage *takeResultTextureColorImage();
    QImage *takeResultTextureNormalImage();
    QImage *takeResultTextureRoughnessImage();
    QImage *takeResultTextureMetalnessImage();
    QImage *takeResultTextureAmbientOcclusionImage();
    Object *takeObject();
    Model *takeResultMesh();
    bool hasTransparencySettings();
    void addPartColorMap(QUuid partId, const QImage *image, float tileScale);
    void addPartNormalMap(QUuid partId, const QImage *image, float tileScale);
    void addPartMetalnessMap(QUuid partId, const QImage *image, float tileScale);
    void addPartRoughnessMap(QUuid partId, const QImage *image, float tileScale);
    void addPartAmbientOcclusionMap(QUuid partId, const QImage *image, float tileScale);
    void generate();
    static QImage *combineMetalnessRoughnessAmbientOcclusionImages(QImage *metalnessImage,
            QImage *roughnessImage,
            QImage *ambientOcclusionImage);
signals:
    void finished();
public slots:
    void process();
public:
    static QColor m_defaultTextureColor;
private:
    void prepare();
private:
    Object *m_object = nullptr;
    QImage *m_resultTextureColorImage = nullptr;
    QImage *m_resultTextureNormalImage = nullptr;
    QImage *m_resultTextureRoughnessImage = nullptr;
    QImage *m_resultTextureMetalnessImage = nullptr;
    QImage *m_resultTextureAmbientOcclusionImage = nullptr;
    Model *m_resultMesh = nullptr;
    std::map<QUuid, std::pair<QImage, float>> m_partColorTextureMap;
    std::map<QUuid, std::pair<QImage, float>> m_partNormalTextureMap;
    std::map<QUuid, std::pair<QImage, float>> m_partMetalnessTextureMap;
    std::map<QUuid, std::pair<QImage, float>> m_partRoughnessTextureMap;
    std::map<QUuid, std::pair<QImage, float>> m_partAmbientOcclusionTextureMap;
    std::set<QUuid> m_countershadedPartIds;
    Snapshot *m_snapshot = nullptr;
    bool m_hasTransparencySettings = false;
    int m_textureSize = Preferences::instance().textureSize();
};

????????首先说明一下该类的数据成员

????????其中以m_result开头的成员都是处理完成的贴图变量或mesh模型变量。

????????以m_part***TextureMap命名的成员都是map类型的键值对关联容器,借助一个唯一的QUuid关键字和相应的pair类型值存储对应通道的贴图。其中pair的first成员为QImage类型图像文件,second成员为float类型的纹理平铺尺度数值。

????????m_object是项目自定义的一种Object对象(详见object.h和object.cpp),它的属性有节点、边、顶点、三角面、法线等几何信息,在生成函数generate()中被使用;m_snapshot是项目使用的快照机制,存储某一时间点软件的画布信息等状态。

????????布尔变量m_hasTransparencySettings用来标记是否有透明度选项。

????????m_textureSize是纹理分辨率,m_countershadedPartIds是一个存储QUuid值的集合容器。

????????看过数据成员后,我们倒回去查看函数成员。函数成员中比较核心的函数有三个:TextureGenerator类的构造函数、预处理函数prepare()以及纹理生成函数generate(),这些函数相对复杂,笔者将在下一篇博客单独进行分析。

????????TextureGenerator的析构函数比较简单,只对数据成员做一些清理操作。

TextureGenerator::~TextureGenerator()
{
    delete m_object;
    delete m_resultTextureColorImage;
    delete m_resultTextureNormalImage;
    delete m_resultTextureRoughnessImage;
    delete m_resultTextureMetalnessImage;
    delete m_resultTextureAmbientOcclusionImage;
    delete m_resultMesh;
    delete m_snapshot;
}

????????下方若干以takeResultTexture开头的函数作用是在完成纹理生成后取出相应的结果。因为它们结构完全一样,这里只列举第一个。该函数定义了一个QImage指针,将生成好的颜色贴图传递给它,然后清空m_resultTextureColorImage的值。

QImage *TextureGenerator::takeResultTextureColorImage()
{
    QImage *resultTextureColorImage = m_resultTextureColorImage;
    m_resultTextureColorImage = nullptr;
    return resultTextureColorImage;
}

????????下面两个函数实现的也是类似的功能,分别传出m_object的值和m_resultMesh的值。

Object *TextureGenerator::takeObject()
{
    Object *object = m_object;
    m_object = nullptr;
    return object;
}

Model *TextureGenerator::takeResultMesh()
{
    Model *resultMesh = m_resultMesh;
    m_resultMesh = nullptr;
    return resultMesh;
}

????????hasTransparencySettings()返回对应的成员值,用于检查是否有透明度设置。

bool TextureGenerator::hasTransparencySettings()
{
    return m_hasTransparencySettings;
}

????????以addPart**Map命名的函数功能比较相似,这里以addPartColorMap()为例。当传入的图像不为空时,调用make_pair方法以*image和titleScale的值创建新的pair对象,以partId为关键字存储在map容器m_partColorTextureMap中。

void TextureGenerator::addPartColorMap(QUuid partId, const QImage *image, float tileScale)
{
    if (nullptr == image)
        return;
    m_partColorTextureMap[partId] = std::make_pair(*image, tileScale);
}

????????combineMetalnessRoughnessAmbientOcclusionImages()是一个纹理整合函数,用于把三个贴图整合到同一张图中。能这么处理的依据是,在图像渲染领域中Metalness、Roughness、AO通道贴图一般使用灰度图,每张图只用一个通道进行表达就足够了。因而可以将一张RGB贴图的三个通道分别用于存储Metalness、Roughness、AO,这样可以节省开销。

????????函数首先检查传入的三张贴图是否为空,然后获取图像尺寸并用255, 255, 0初始化结果图片textureMetalnessRoughnessAmbientOcclusionImage。接着遍历每一个像素,把Metalness贴图、Roughness贴图、AO贴图对应坐标处的灰度值分别放到B通道、G通道、R通道,最后返回textureMetalnessRoughnessAmbientOcclusionImage的值。

QImage *TextureGenerator::combineMetalnessRoughnessAmbientOcclusionImages(QImage *metalnessImage,
        QImage *roughnessImage,
        QImage *ambientOcclusionImage)
{
    QImage *textureMetalnessRoughnessAmbientOcclusionImage = nullptr;
    if (nullptr != metalnessImage ||
            nullptr != roughnessImage ||
            nullptr != ambientOcclusionImage) {
        int textureSize = 0;
        if (nullptr != metalnessImage)
            textureSize = metalnessImage->height();
        if (nullptr != roughnessImage)
            textureSize = roughnessImage->height();
        if (nullptr != ambientOcclusionImage)
            textureSize = ambientOcclusionImage->height();
        if (textureSize > 0) {
            textureMetalnessRoughnessAmbientOcclusionImage = new QImage(textureSize, textureSize, QImage::Format_ARGB32);
            textureMetalnessRoughnessAmbientOcclusionImage->fill(QColor(255, 255, 0));
            for (int row = 0; row < textureMetalnessRoughnessAmbientOcclusionImage->height(); ++row) {
                for (int col = 0; col < textureMetalnessRoughnessAmbientOcclusionImage->width(); ++col) {
                    QColor color(255, 255, 0);
                    if (nullptr != metalnessImage)
                        color.setBlue(qGray(metalnessImage->pixel(col, row)));
                    if (nullptr != roughnessImage)
                        color.setGreen(qGray(roughnessImage->pixel(col, row)));
                    if (nullptr != ambientOcclusionImage)
                        color.setRed(qGray(ambientOcclusionImage->pixel(col, row)));
                    textureMetalnessRoughnessAmbientOcclusionImage->setPixelColor(col, row, color);
                }
            }
        }
    }
    return textureMetalnessRoughnessAmbientOcclusionImage;
}

未完待续...

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-10-19 12:13:25  更:2021-10-19 12:15:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 0:55:07-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码