使用Assimp加载glb/gltf文件,然后使用Qt3D来渲染

news2024/9/22 9:58:41

文章目录

  • 1.代码
  • 2.说明
    • 2.1.调用
    • 2.2.关于贴图

1.代码

ModelLoader.h

#ifndef MODELLOADER_H
#define MODELLOADER_H

#include <QObject>
#include <Qt3DRender>
#include <QVector3D>
#include <QGeometry>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

struct Triangle
{
    int vertexIndex1 { 0 };
    int vertexIndex2 { 0 };
    int vertexIndex3 { 0 };

    bool operator==(const Triangle &rhs) const {
        return vertexIndex1 == rhs.vertexIndex1 &&
               vertexIndex2 == rhs.vertexIndex2 &&
               vertexIndex3 == rhs.vertexIndex3;
    }
};

namespace RenderAttributes
{
Qt3DRender::QAttribute *create(const QVector<Triangle> &triangles, Qt3DRender::QGeometry *parent);
Qt3DRender::QAttribute *create(const QVector<QVector3D> &vertices, const QString &name, Qt3DRender::QGeometry *parent);
Qt3DRender::QAttribute *clone(Qt3DRender::QAttribute *from, Qt3DRender::QGeometry *parent);
}


class ModelLoader
{
public:
    ModelLoader();

    // 加载模型
    static int loadModelFromFile(Qt3DCore::QEntity *rootEntity, QString filePath);

private:
    static int processNode(aiNode *node, const aiScene *scene, Qt3DCore::QEntity *entity);
    static int processMesh(aiMesh *mesh, const aiScene *scene, Qt3DCore::QEntity *entity);
};

class MyQPaintedTextureImage : public Qt3DRender::QPaintedTextureImage
{
public:
    void setImage(QImage &i){
        image = i;
        setSize(i.size());
    }
    virtual void paint(QPainter *painter) override{
        painter->drawImage(0, 0, image);
    }

private:
    QImage image;
};
#endif // MODELLOADER_H

ModelLoader.cpp

#include "modelloader.h"
#include <Qt3DExtras>
#include <Qt3DRender>
#include <QPaintedTextureImage>

Qt3DRender::QAttribute *RenderAttributes::create(const QVector<Triangle> &triangles, Qt3DRender::QGeometry *parent)
{
    auto attribute = new Qt3DRender::QAttribute(parent);

    QVector<uint> indices;
    indices.reserve(triangles.size() * 3);

    for (const Triangle &triangle : triangles) {
        indices << static_cast<uint>(triangle.vertexIndex1)
        << static_cast<uint>(triangle.vertexIndex2)
        << static_cast<uint>(triangle.vertexIndex3);
    }

    Qt3DRender::QBuffer *dataBuffer = new Qt3DRender::QBuffer(attribute);
    const int rawSize = indices.size() * static_cast<int>(sizeof(uint));
    auto rawData = QByteArray::fromRawData(reinterpret_cast<const char*>(indices.constData()), rawSize);
    rawData.detach();
    dataBuffer->setData(rawData);

    attribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
    attribute->setBuffer(dataBuffer);
    attribute->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt);
    attribute->setVertexSize(1);
    attribute->setByteOffset(0);
    attribute->setByteStride(sizeof(uint));
    attribute->setCount(static_cast<uint>(indices.size()));

    return attribute;
}
Qt3DRender::QAttribute *RenderAttributes::create(const QVector<QVector3D> &vertices, const QString &name, Qt3DRender::QGeometry *parent)
{
    auto attribute = new Qt3DRender::QAttribute(parent);

    QVector<float> values;
    values.reserve(vertices.size() * 3);

    for (const QVector3D &v : vertices) {
        values << v.x() << v.y() << v.z();
    }

    Qt3DRender::QBuffer *dataBuffer = new Qt3DRender::QBuffer(attribute);
    const int rawSize = values.size() * static_cast<int>(sizeof(float));
    auto rawData = QByteArray::fromRawData(reinterpret_cast<const char*>(values.constData()), rawSize);
    rawData.detach();
    dataBuffer->setData(rawData);

    attribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    attribute->setBuffer(dataBuffer);
    attribute->setVertexBaseType(Qt3DRender::QAttribute::Float);
    attribute->setVertexSize(3);
    attribute->setByteOffset(0);
    attribute->setByteStride(3 * sizeof(float));
    attribute->setName(name);
    attribute->setCount(static_cast<uint>(vertices.size()));

    return attribute;
}

uint qHash(const QVector3D &key, uint seed = 0)
{
    if (key.isNull())
    {
        return seed;
    }
    else
    {
        float array[3] = {key.x(), key.y(), key.z()};
        return qHashBits(array, 3 * sizeof(int), seed);
    }
}

Qt3DRender::QAttribute *RenderAttributes::clone(Qt3DRender::QAttribute *from, Qt3DRender::QGeometry *parent)
{
    auto attribute = new Qt3DRender::QAttribute(parent);

    Qt3DRender::QBuffer *dataBuffer = new Qt3DRender::QBuffer(attribute);
    auto dataCopy = from->buffer()->data();
    dataCopy.detach();
    dataBuffer->setData(dataCopy);

    attribute->setAttributeType(from->attributeType());
    attribute->setBuffer(dataBuffer);
    attribute->setVertexBaseType(from->vertexBaseType());
    attribute->setVertexSize(from->vertexSize());
    attribute->setByteOffset(from->byteOffset());
    attribute->setByteStride(from->byteStride());
    attribute->setName(from->name());
    attribute->setCount(from->count());

    return attribute;
}

ModelLoader::ModelLoader() {}

int ModelLoader::loadModelFromFile(Qt3DCore::QEntity *rootEntity, QString filePath)
{
    Assimp::Importer importer;

    std::string modelPath = filePath.toStdString();
    const aiScene* sceneObjPtr = importer.ReadFile(modelPath,
                                                   aiProcess_Triangulate | aiProcess_FlipUVs);
    // const aiScene* sceneObjPtr = importer.ReadFile(filePath,
    //                                                aiProcess_FlipUVs);
    if (!sceneObjPtr
        || sceneObjPtr->mFlags == AI_SCENE_FLAGS_INCOMPLETE
        || !sceneObjPtr->mRootNode)
    {
        // 加载模型失败
        qDebug() << "Error:Model::loadModel, description: "
                 << importer.GetErrorString();
        return -1;
    }

    qDebug() << "load object:" << sceneObjPtr->mNumMeshes << sceneObjPtr->mNumAnimations;

    // 是否存在动画
    if(sceneObjPtr->HasAnimations())
    {
        aiAnimation* animation = sceneObjPtr->mAnimations[0];
        qDebug() << animation->mDuration << animation->mTicksPerSecond << animation->mName.C_Str();
    }

    // 是否存在材质
    if(sceneObjPtr->HasMaterials())
    {
        qDebug() << sceneObjPtr->mMaterials[0]->GetName().C_Str();
    }

    // 是否存在网格模型
    if(sceneObjPtr->HasMeshes())
    {
        qDebug() << sceneObjPtr->mMeshes[0]->mName.C_Str();
    }

    Qt3DCore::QEntity *modelEntity = new Qt3DCore::QEntity(rootEntity);

    // 修正一下坐标系
    Qt3DCore::QTransform *mTransform = new Qt3DCore::QTransform();
    mTransform->setRotationX(-90);
    modelEntity->addComponent(mTransform);

    return processNode(sceneObjPtr->mRootNode, sceneObjPtr, modelEntity);
}

int ModelLoader::processNode(aiNode *node, const aiScene *scene, Qt3DCore::QEntity *entity)
{
    qDebug() << "node:" << node->mName.C_Str() << node->mNumMeshes << node->mNumChildren;

    Qt3DCore::QEntity *partModel = new Qt3DCore::QEntity(entity);
    Qt3DCore::QTransform *partTransform = new Qt3DCore::QTransform;
    aiMatrix4x4 mat = node->mTransformation;
    QMatrix4x4 qMat = QMatrix4x4(mat.a1, mat.a2, mat.a3, mat.a4,
                                 mat.b1, mat.b2, mat.b3, mat.b4,
                                 mat.c1, mat.c2, mat.c3, mat.c4,
                                 mat.d1, mat.d2, mat.d3, mat.d4
                                 );
    partTransform->setMatrix(qMat);
    partModel->addComponent(partTransform);
    partModel->setObjectName(node->mName.C_Str()); // 这个很有用,可以用来后期对节点进行查找

    // qDebug() << partTransform->rotationX()
    //          << partTransform->rotationY()
    //          << partTransform->rotationZ();

     // process each mesh located at the current node
    for (unsigned int i = 0; i < node->mNumMeshes; i++)
    {
        // the node object only contains indices to index the actual objects in the scene.
        // the scene contains all the data, node is just to keep stuff organized (like relations between nodes).
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        processMesh(mesh, scene, partModel);
    }

    // after we've processed all of the meshes (if any) we then recursively process each of the children nodes
    for (unsigned int i = 0; i < node->mNumChildren; i++)
    {
        processNode(node->mChildren[i], scene, partModel);
    }

    return 0;
}

int ModelLoader::processMesh(aiMesh *mesh, const aiScene *scene, Qt3DCore::QEntity *entity)
{
    qDebug() << "---> mesh:" << mesh->mName.C_Str();;

    Qt3DRender::QGeometry *geometry = new Qt3DRender::QGeometry(entity);

    Qt3DRender::QGeometryRenderer *geoRen = new Qt3DRender::QGeometryRenderer(entity);
    geoRen->setGeometry(geometry);
    geoRen->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);


    // 如果网格包含法线,则添加法线节点
    if(mesh->HasNormals())
    {
        // qDebug() << "has normal:" << mesh->mNumVertices;

        // 法线数据
        QVector<QVector3D> normals;
        for (unsigned int i = 0; i < mesh->mNumVertices; i++)
        {
            aiVector3D normal = mesh->mNormals[i];
            normals << QVector3D(normal.x, normal.y, normal.z);
        }

        auto attr = RenderAttributes::create(normals, "vertexNormal", geometry);
        geometry->addAttribute(attr);
    }

    // 如果网格包含纹理坐标,则添加纹理坐标节点
    if(mesh->HasTextureCoords(0))
    {
        // qDebug() << "NumUVComponents :" << mesh->mNumUVComponents;
        // if(mesh->mNumUVComponents > 0)
        // {
        //     qDebug() << "has texture"
        //              << mesh->mTextureCoordsNames[0]->C_Str()
        //         ;
        // }

        QVector<QVector3D> textureCoordinates;
        for (unsigned int i = 0; i < mesh->mNumVertices; i++)
        {
            aiVector3D texCoord = mesh->mTextureCoords[0][i];
            textureCoordinates << QVector3D(texCoord.x, texCoord.y, texCoord.z);
        }
        auto attrTC = RenderAttributes::create(textureCoordinates, Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName(), geometry);
        geometry->addAttribute(attrTC);
    }


    // 将Assimp网格的顶点坐标添加到坐标节点
    // qDebug() << "vertices:" << mesh->mNumVertices;
    QVector<QVector3D> vertices;
    for (unsigned int i = 0; i < mesh->mNumVertices; i++)
    {
        aiVector3D vertice = mesh->mVertices[i];
        vertices << QVector3D(vertice.x, vertice.y, vertice.z);
    }
    auto attrVeti = RenderAttributes::create(vertices, Qt3DRender::QAttribute::defaultPositionAttributeName(), geometry);
    geometry->addAttribute(attrVeti);
    geometry->setBoundingVolumePositionAttribute(attrVeti);

    // 将Assimp网格的面索引添加到面索引
    // qDebug() << "faces:" << mesh->mNumFaces;
    QVector<Triangle> faces;
    for (unsigned int i = 0; i < mesh->mNumFaces; i++)
    {
        Triangle triFace;

        aiFace face = mesh->mFaces[i];
        triFace.vertexIndex1 = face.mIndices[0];
        triFace.vertexIndex2 = face.mIndices[1];
        triFace.vertexIndex3 = face.mIndices[2];

        faces << triFace;

        // for (unsigned int j = 0; j < face.mNumIndices; j++) // 填充面的索引
        // {
        //     faceSet->coordIndex.set1Value(faceSet->coordIndex.getNum(), face.mIndices[j]);

        // }
    }
    auto attrFace = RenderAttributes::create(faces, geometry);
    geometry->addAttribute(attrFace);

    // 设置材质
    if(mesh->mMaterialIndex >= 0)
    {
        qDebug() << "set material";
        aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];

        // 创建材质节点

        // // 使用QMetalRoughMaterial时,貌似亮度有点不对
        // Qt3DExtras::QMetalRoughMaterial *qMaterial = new Qt3DExtras::QMetalRoughMaterial();
        // qMaterial->setMetalness(0.4);
        // qMaterial->setRoughness(0.55);

        Qt3DExtras::QDiffuseMapMaterial *qMaterial = new Qt3DExtras::QDiffuseMapMaterial();

        // 设置材质漫反射度
        float shiness;
        if (AI_SUCCESS == material->Get(AI_MATKEY_SHININESS, shiness)) {
            qDebug() << "shiness:" << shiness;

            // 使用 shiness 值
            qMaterial->setShininess(shiness);
        }

        // 设置材质漫反射颜色
        aiColor4D diffuseColor;
        if(AI_SUCCESS == material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor))
        {
            qDebug() << "rgb:" << diffuseColor.r << diffuseColor.g << diffuseColor.b;

            QColor tmpColor = QColor(diffuseColor.r * 255.0,
                                     diffuseColor.g * 255.0,
                                     diffuseColor.b * 255.0);
            qDebug() << tmpColor << QColor(255, 255, 255);

            // qMaterial->setBaseColor(tmpColor);

            QImage img(100, 100, QImage::Format_ARGB32);
            img.fill(tmpColor);
            MyQPaintedTextureImage *txtImg = new MyQPaintedTextureImage();
            txtImg->setImage(img);
            Qt3DRender::QTexture2D *txt2d = new Qt3DRender::QTexture2D();
            txt2d->addTextureImage(txtImg);
            // qMaterial->setBaseColor(QVariant::fromValue(txt2d));
            qMaterial->setDiffuse(txt2d);
        }

        // 设置环境颜色
        aiColor4D ambientColor;
        if(AI_SUCCESS == material->Get(AI_MATKEY_COLOR_AMBIENT, ambientColor))
        {
            QColor tmpColor = QColor(ambientColor.r, ambientColor.g, ambientColor.b, ambientColor.a);
            qDebug() << "ambient color:" << tmpColor;
            qMaterial->setAmbient(tmpColor);
        }
        else
        {
            qMaterial->setAmbient(QColor(128, 128, 128));
        }

        // 如果材质包含纹理,则创建纹理节点
        aiString texturePath; //Assimp texture file path
        if(AI_SUCCESS == material->GetTexture(aiTextureType_DIFFUSE, 0, &texturePath))
        {
            qDebug() << "texture file:" << texturePath.C_Str();

            const aiTexture *embTexture = scene->GetEmbeddedTexture(texturePath.C_Str());
            if(embTexture) // 假如内嵌的纹理
            {
                qDebug() << "has embed img:"
                         << (char*)embTexture->achFormatHint
                         << embTexture->mFilename.C_Str()
                         << embTexture->mWidth << embTexture->mHeight;

                // 利用QPaintedTextureImage可以实现加载内存中的图片
                const unsigned char* data = reinterpret_cast<const unsigned char*>(embTexture->pcData);
                const size_t size = embTexture->mWidth;
                QByteArray imageData((char*)data, size);
                QImage img;
                if(img.loadFromData(imageData)){
                    qDebug() << img;
                    MyQPaintedTextureImage *txtImg = new MyQPaintedTextureImage();
                    txtImg->setImage(img);

                    Qt3DRender::QTexture2D *txt2d = new Qt3DRender::QTexture2D();
                    txt2d->addTextureImage(txtImg);
                    // qMaterial->setBaseColor(QVariant::fromValue(txt2d));
                    qMaterial->setDiffuse(txt2d);
                }
            }
            else
            {
                qDebug() << "-----no embed img";
            }
        }
        else
        {
            qDebug() << "get texture fail" << material->GetTextureCount(aiTextureType_DIFFUSE);
        }

        entity->addComponent(qMaterial);

    }

    entity->addComponent(geoRen);

    return 0;
}

2.说明

2.1.调用

Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow();
view->defaultFrameGraph()->setClearColor(QColor(QRgb(0x2b2b2b)));

QString filePath = "myModel.glb";
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
ModelLoader::loadModelFromFile(rootEntity, filePath);

view->setRootEntity(rootEntity);

// 灯光及其他设置

2.2.关于贴图

假如使用QTextureImage的话,只能加载本地图片,但是参考【How can I load a QPaintedTextureImage into a QTextureMaterial?】,可以使用QPaintedTextureImage来加载内存中的图片。而glb/gltf文件本身包含了图片内容,读取glb文件时,图片内容已经加载到内存中了,因此QPaintedTextureImage更加适合。


参考
【Qt3DExample】
【How can I load a QPaintedTextureImage into a QTextureMaterial?】

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于LSTM的温度时序预测

1.背景 本文接【时序预测SARIMAX模型】 一文&#xff0c;采用LSTM模型进行平均温度数据预测。具体的背景和数据分析就不做重复说明&#xff0c;感兴趣可以去看上文即可。 2.LSTM模型 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;是一种特殊…

AI驱动TDSQL-C Serverless 数据库技术实战营-ai学生选课系统数据分析

以前用过腾讯的TDSQL-MYSQL&#xff0c;TBASE&#xff0c;最近了解到TDSQL-C serverless&#xff0c;本次试验结合的AI大模型驱动来学习实战TDSQL-C serverless&#xff0c;体验服务化的数据库&#xff0c;和一句简单描述进行学生选课系统数据分析&#xff1b; 我使用的分析数据…

C++初阶-list用法总结

目录 1.迭代器的分类 2.算法举例 3.push_back/emplace_back 4.insert/erase函数介绍 5.splice函数介绍 5.1用法一&#xff1a;把一个链表里面的数据给另外一个链表 5.2 用法二&#xff1a;调整链表当前的节点数据 6.unique去重函数介绍 1.迭代器的分类 我们的这个迭代器…

【alluxio编译报错】Some files do not have the expected license header

Some files do not have the expected license header 快捷导航 在开始解决问题之前&#xff0c;大家可以通过下面的导航快速找到相关资源啦&#xff01;&#x1f4a1;&#x1f447; 快捷导航链接地址备注相关文档-ambaribigtop自定义组件集成https://blog.csdn.net/TTBIGDA…

【Elasticsearch系列十八】Ik 分词器

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

通信入门系列书籍推荐一:通信原理和通信原理学习辅导

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、背景 二、通信原理 …

石岩体育馆附近的免费停车场探寻

坐标&#xff1a;石岩体育馆侧的石清大道断头路, 如果运气好的话&#xff0c;遇到刚好有车开出的话&#xff0c;我觉得可以作为中长期的免费停车点 第一次路过的时候&#xff0c;把我震惊了&#xff0c;我一直以为石岩停车位紧张比市区还严重&#xff0c;因为石岩大部分为统建楼…

python画图|图像背景颜色设置

python画图出来的默认图形背景是白色&#xff0c;有时候并不适合大家表达想要表达的意思。 因此&#xff0c;我们很有必要掌握自己设置图形背景颜色的技巧。 【1】官网教程 首先请各位看官移步官网&#xff0c;看看官网如何设置&#xff0c;下述链接可轻松到达&#xff1a; …

Lubuntu电源管理

lxqt-config-powermanagement 打开托盘图标 Show icon 电源管理 电源管理管理笔记本电脑电池的低电量、关闭笔记本电脑盖的操作以及计算机长时间闲置时应采取的措施。 用法 LXQt 电源管理会监控您的电池、笔记本电脑盖、空闲情况&#xff0c;以及当您按下电源或睡眠按钮时会发…

IS-ISv6单拓扑存在的问题

文章目录 IS-ISv6单拓扑配置单拓扑存在的问题解决 IS-ISv6单拓扑B站视频传送门 IS-ISv6单拓扑 配置 R1&#xff1a;sy sy R1 ipv6 inter g0/0/0 ip add 12.1.1.1 24 ipv6 enable ipv add 2001:12::1 64 inter loop0 ip add 1.1.1.1 32 ipv6 enable ipv address 2002::1 128isi…

30个GPT提示词天花板,一小时从大纲到终稿

PROMPT 1 中文&#xff1a;构建研究背景与意义&#xff0c;阐述研究问题的紧迫性和重要性。 English: Establish the research background and significance, elucidating the urgency and importance of the research question. 中文&#xff1a;设计研究目的与目标&#xff…

TDOA方法求二维坐标的MATLAB代码演示与讲解

引言 时间差定位(Time Difference of Arrival, TDOA)是一种用于确定信号源位置的技术,广泛应用于无线通信、声学定位等领域。通过测量信号到达多个接收器的时间差,可以计算出信号源的二维坐标。本文将通过MATLAB代码演示如何使用TDOA方法来求解二维坐标。 TDOA原理 TDOA…

LeetCode题练习与总结:回文链表--234

一、题目描述 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true示例 2&#xff1a; 输入&#x…

CocosCreator 3.x 实现角色移动与加载时动态屏幕边缘检测

效果 思路 通过cc.view全局单例 View 对象获取屏幕尺寸加载时根据屏幕尺寸动态计算上下左右边缘 代码实现 import { _decorator, Component, EventTouch, Input, input, Node, view } from cc; const { ccclass, property } _decorator;/*** 玩家控制脚本*/ ccclass(Player…

Linux之实战命令03:stat应用实例(三十七)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

树及二叉树(选择题)

树 在树中&#xff0c;总结点数为所有结点的度和再加一 5、设一棵度为3的树&#xff0c;其中度为2&#xff0c;1.0的结点数分别为3&#xff0c;1&#xff0c;6。该树中度为3 的结点数为_。 二叉树 设二叉树的所有节点个数为N&#xff0c;度为零的结点&#xff08;叶子结点…

P9235 [蓝桥杯 2023 省 A] 网络稳定性

*原题链接* 最小瓶颈生成树题&#xff0c;和货车运输完全一样。 先简化题意&#xff0c; 次询问&#xff0c;每次给出 &#xff0c;问 到 的所有路径集合中&#xff0c;最小边权的最大值。 对于这种题可以用kruskal生成树来做&#xff0c;也可以用倍增来写&#xff0c;但不…

数字基带之相移键控PSK

1 相移键控定义 相移键控是指用载波的相移位变化来传递信号&#xff0c;不改变载波的幅度和频率&#xff0c;可用下面的公式表示。 是载波的幅度&#xff0c;是载波的角频率&#xff0c;是载波的瞬时相位&#xff0c;是载波的初始相位。如果需要调制的信号为1bit的二进制数&am…

spark读取数据性能提升

1. 背景 spark默认的jdbc只会用单task读取数据&#xff0c;读取大数据量时&#xff0c;效率低。 2. 解决方案 根据分区字段&#xff0c;如日期进行划分&#xff0c;增加task数量提升效率。 /*** 返回每个task按时间段划分的过滤语句* param startDate* param endDate* param …

[Web安全 网络安全]-CSRF跨站请求伪造

文章目录&#xff1a; 一&#xff1a;前言 1.定义 2.攻击原理 3.危害 4.环境 4.1 靶场 4.2 扫描工具 5.cookie session token的区别 6.CSRF与XSS的区别 二&#xff1a;构建CSRF的payload GET请求&#xff1a;a标签 img标签 POST请求&#xff1a;form表单 三&…