Dust3D开源项目分析——渲染与材质部分 | 纹理生成 Part5

该博客详细介绍了三维图形渲染中溶解性贴图的绘制过程,涉及三角形处理、邻域半边结构填充以及纹理生成的算法。在处理过程中,遇到无效的三角形和找不到的UV矩形时,会进行异常处理并跳过。最后,通过Qt进行纹理的生成和清理,完成整个渲染任务。
摘要由CSDN通过智能技术生成

 2021SC@SDUSC

auto drawBySolubility = [&](const QUuid &partId, size_t triangleIndex, size_t firstVertexIndex, size_t secondVertexIndex,
            const QUuid &neighborPartId) {
        const std::vector<QVector2D> &uv = triangleVertexUvs[triangleIndex];
        const auto &allRects = partUvRects.find(partId);
        if (allRects == partUvRects.end()) {
            qDebug() << "Found part uv rects failed";
            return;
        }
        const auto &firstPoint = uv[firstVertexIndex];
        const auto &secondPoint = uv[secondVertexIndex];
        auto edgeLength = firstPoint.distanceToPoint(secondPoint);
        auto middlePoint = (firstPoint + secondPoint) / 2.0;
        float alpha = 1.0;
        const auto &findColor = partColorMap.find(partId);
        if (findColor == partColorMap.end())
            return;
        alpha = findColor->second.alphaF();
        const auto &findNeighborColorSolubility = partColorSolubilityMap.find(neighborPartId);
        if (findNeighborColorSolubility == partColorSolubilityMap.end())
            return;
        if (qFuzzyIsNull(findNeighborColorSolubility->second))
            return;
        const auto &findNeighborColor = partColorMap.find(neighborPartId);
        if (findNeighborColor == partColorMap.end())
            return;
        for (const auto &it: allRects->second) {
            if (it.contains(firstPoint.x(), firstPoint.y()) ||
                    it.contains(secondPoint.x(), secondPoint.y())) {
                float finalRadius = (it.width() + it.height()) * 0.5 * findNeighborColorSolubility->second;
                if (finalRadius < edgeLength)
                    finalRadius = edgeLength;
                QRectF fillTarget((middlePoint.x() - finalRadius),
                    (middlePoint.y() - finalRadius),
                    (finalRadius + finalRadius),
                    (finalRadius + finalRadius));
                auto clippedRect = it.intersected(fillTarget);
                QRectF translatedRect = {
                    clippedRect.left() * TextureGenerator::m_textureSize,
                    clippedRect.top() * TextureGenerator::m_textureSize,
                    clippedRect.width() * TextureGenerator::m_textureSize,
                    clippedRect.height() * TextureGenerator::m_textureSize
                };
                texturePainter.setOpacity(alpha);
                auto findTextureResult = partColorTexturePixmaps.find(neighborPartId);
                if (findTextureResult != partColorTexturePixmaps.end()) {
                    const auto &pixmap = findTextureResult->second.first;
                    const auto &rotatedPixmap = findTextureResult->second.second;
                    
                    QImage tmpImage(translatedRect.width(), translatedRect.height(), QImage::Format_ARGB32);
                    QPixmap tmpPixmap = QPixmap::fromImage(tmpImage);
                    QPainter tmpPainter;
                    QRectF tmpImageFrame = QRectF(0, 0, translatedRect.width(), translatedRect.height());
                    
                    // Fill tiled texture
                    tmpPainter.begin(&tmpPixmap);
                    tmpPainter.setOpacity(alpha);
                    if (it.width() < it.height()) {
                        tmpPainter.drawTiledPixmap(tmpImageFrame, rotatedPixmap, QPointF(translatedRect.top(), translatedRect.left()));
                    } else {
                        tmpPainter.drawTiledPixmap(tmpImageFrame, pixmap, translatedRect.topLeft());
                    }
                    tmpPainter.setOpacity(1.0);
                    tmpPainter.end();
                    
                    // Apply gradient
                    QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize - translatedRect.left(),
                        middlePoint.y() * TextureGenerator::m_textureSize - translatedRect.top()),
                        finalRadius * TextureGenerator::m_textureSize);
                    gradient.setColorAt(0.0, findNeighborColor->second);
                    gradient.setColorAt(1.0, Qt::transparent);
                    
                    tmpPainter.begin(&tmpPixmap);
                    tmpPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
                    tmpPainter.fillRect(tmpImageFrame, gradient);
                    tmpPainter.end();
                    
                    texturePainter.drawPixmap(translatedRect, tmpPixmap, tmpImageFrame);
                } else {
                    QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize,
                        middlePoint.y() * TextureGenerator::m_textureSize),
                        finalRadius * TextureGenerator::m_textureSize);
                    gradient.setColorAt(0.0, findNeighborColor->second);
                    gradient.setColorAt(1.0, Qt::transparent);
                    texturePainter.fillRect(translatedRect, gradient);
                }
                texturePainter.setOpacity(1.0);
                break;
            }
        }
    };
    
    std::map<std::pair<size_t, size_t>, std::tuple<size_t, size_t, size_t>> halfEdgeToTriangleMap;
    for (size_t i = 0; i < m_object->triangles.size(); ++i) {
        const auto &triangleIndices = m_object->triangles[i];
        if (triangleIndices.size() != 3) {
            qDebug() << "Found invalid triangle indices";
            continue;
        }
        for (size_t j = 0; j < triangleIndices.size(); ++j) {
            size_t k = (j + 1) % triangleIndices.size();
            halfEdgeToTriangleMap.insert(std::make_pair(std::make_pair(triangleIndices[j], triangleIndices[k]),
                std::make_tuple(i, j, k)));
        }
    }
    for (const auto &it: halfEdgeToTriangleMap) {
        auto oppositeHalfEdge = std::make_pair(it.first.second, it.first.first);
        const auto &opposite = halfEdgeToTriangleMap.find(oppositeHalfEdge);
        if (opposite == halfEdgeToTriangleMap.end())
            continue;
        const std::pair<QUuid, QUuid> &source = triangleSourceNodes[std::get<0>(it.second)];
        const std::pair<QUuid, QUuid> &oppositeSource = triangleSourceNodes[std::get<0>(opposite->second)];
        if (source.first == oppositeSource.first)
            continue;
        drawBySolubility(source.first, std::get<0>(it.second), std::get<1>(it.second), std::get<2>(it.second), oppositeSource.first);
        drawBySolubility(oppositeSource.first, std::get<0>(opposite->second), std::get<1>(opposite->second), std::get<2>(opposite->second), source.first);
    }
    
    // Draw belly white
    texturePainter.setCompositionMode(QPainter::CompositionMode_SoftLight);
    for (size_t triangleIndex = 0; triangleIndex < m_object->triangles.size(); ++triangleIndex) {
        const auto &normal = triangleNormals[triangleIndex];
        const std::pair<QUuid, QUuid> &source = triangleSourceNodes[triangleIndex];
        const auto &partId = source.first;
        if (m_countershadedPartIds.find(partId) == m_countershadedPartIds.end())
            continue;
        
        const auto &allRects = partUvRects.find(partId);
        if (allRects == partUvRects.end()) {
            qDebug() << "Found part uv rects failed";
            continue;
        }
        
        const auto &findObjectNode = nodeMap.find(source);
        if (findObjectNode == nodeMap.end())
            continue;
        const ObjectNode *objectNode = findObjectNode->second;
        if (qAbs(QVector3D::dotProduct(objectNode->direction, QVector3D(0, 1, 0))) >= 0.707) {
            if (QVector3D::dotProduct(normal, QVector3D(0, 0, 1)) <= 0.0)
                continue;
        } else {
            if (QVector3D::dotProduct(normal, QVector3D(0, -1, 0)) <= 0.0)
                continue;
        }
        
        const auto &triangleIndices = m_object->triangles[triangleIndex];
        if (triangleIndices.size() != 3) {
            qDebug() << "Found invalid triangle indices";
            continue;
        }
        
        const std::vector<QVector2D> &uv = triangleVertexUvs[triangleIndex];
        QVector2D middlePoint = (uv[0] + uv[1] + uv[2]) / 3.0;
        float finalRadius = (uv[0].distanceToPoint(uv[1]) +
            uv[1].distanceToPoint(uv[2]) +
            uv[2].distanceToPoint(uv[0])) / 3.0;
        QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize,
            middlePoint.y() * TextureGenerator::m_textureSize),
            finalRadius * TextureGenerator::m_textureSize);
        gradient.setColorAt(0.0, Qt::white);
        gradient.setColorAt(1.0, Qt::transparent);
        for (const auto &it: allRects->second) {
            if (it.contains(middlePoint.x(), middlePoint.y())) {
                QRectF fillTarget((middlePoint.x() - finalRadius),
                    (middlePoint.y() - finalRadius),
                    (finalRadius + finalRadius),
                    (finalRadius + finalRadius));
                auto clippedRect = it.intersected(fillTarget);
                QRectF translatedRect = {
                    clippedRect.left() * TextureGenerator::m_textureSize,
                    clippedRect.top() * TextureGenerator::m_textureSize,
                    clippedRect.width() * TextureGenerator::m_textureSize,
                    clippedRect.height() * TextureGenerator::m_textureSize
                };
                texturePainter.fillRect(translatedRect, gradient);
            }
        }
        
        // Fill the neighbor halfedges
        for (int i = 0; i < 3; ++i) {
            int j = (i + 1) % 3;
            auto oppositeHalfEdge = std::make_pair(triangleIndices[j], triangleIndices[i]);
            const auto &opposite = halfEdgeToTriangleMap.find(oppositeHalfEdge);
            if (opposite == halfEdgeToTriangleMap.end())
                continue;
            auto oppositeTriangleIndex = std::get<0>(opposite->second);
            const std::pair<QUuid, QUuid> &oppositeSource = triangleSourceNodes[oppositeTriangleIndex];
            if (partId == oppositeSource.first)
                continue;
            const auto &oppositeAllRects = partUvRects.find(oppositeSource.first);
            if (oppositeAllRects == partUvRects.end()) {
                qDebug() << "Found part uv rects failed";
                continue;
            }
            const std::vector<QVector2D> &oppositeUv = triangleVertexUvs[oppositeTriangleIndex];
            QVector2D oppositeMiddlePoint = (oppositeUv[std::get<1>(opposite->second)] + oppositeUv[std::get<2>(opposite->second)]) * 0.5;
            QRadialGradient oppositeGradient(QPointF(oppositeMiddlePoint.x() * TextureGenerator::m_textureSize,
                oppositeMiddlePoint.y() * TextureGenerator::m_textureSize),
                finalRadius * TextureGenerator::m_textureSize);
            oppositeGradient.setColorAt(0.0, Qt::white);
            oppositeGradient.setColorAt(1.0, Qt::transparent);
            for (const auto &it: oppositeAllRects->second) {
                if (it.contains(oppositeMiddlePoint.x(), oppositeMiddlePoint.y())) {
                    QRectF fillTarget((oppositeMiddlePoint.x() - finalRadius),
                        (oppositeMiddlePoint.y() - finalRadius),
                        (finalRadius + finalRadius),
                        (finalRadius + finalRadius));
                    auto clippedRect = it.intersected(fillTarget);
                    QRectF translatedRect = {
                        clippedRect.left() * TextureGenerator::m_textureSize,
                        clippedRect.top() * TextureGenerator::m_textureSize,
                        clippedRect.width() * TextureGenerator::m_textureSize,
                        clippedRect.height() * TextureGenerator::m_textureSize
                    };
                    texturePainter.fillRect(translatedRect, oppositeGradient);
                }
            }
        }
    }
    
    hasNormalMap = !m_partNormalTextureMap.empty();
    if (!m_partMetalnessTextureMap.empty())
        hasMetalnessMap = true;
    if (!m_partRoughnessTextureMap.empty())
        hasRoughnessMap = true;
    hasAmbientOcclusionMap = !m_partAmbientOcclusionTextureMap.empty();
    
    auto paintTextureEndTime = countTimeConsumed.elapsed();

    texturePainter.end();
    textureNormalPainter.end();
    textureMetalnessPainter.end();
    textureRoughnessPainter.end();
    textureAmbientOcclusionPainter.end();
    
    if (!hasNormalMap) {
        delete m_resultTextureNormalImage;
        m_resultTextureNormalImage = nullptr;
    }
    
    if (!hasMetalnessMap && !hasRoughnessMap && !hasAmbientOcclusionMap) {
        delete m_resultTextureMetalnessImage;
        m_resultTextureMetalnessImage = nullptr;

        delete m_resultTextureRoughnessImage;
        m_resultTextureRoughnessImage = nullptr;

        delete m_resultTextureAmbientOcclusionImage;
        m_resultTextureAmbientOcclusionImage = nullptr;
    }
    
    auto createResultBeginTime = countTimeConsumed.elapsed();
    m_resultMesh->setTextureImage(new QImage(*m_resultTextureColorImage));
    if (nullptr != m_resultTextureNormalImage)
        m_resultMesh->setNormalMapImage(new QImage(*m_resultTextureNormalImage));
    if (hasMetalnessMap || hasRoughnessMap || hasAmbientOcclusionMap) {
        m_resultMesh->setMetalnessRoughnessAmbientOcclusionImage(combineMetalnessRoughnessAmbientOcclusionImages(
            m_resultTextureMetalnessImage,
            m_resultTextureRoughnessImage,
            m_resultTextureAmbientOcclusionImage));
        m_resultMesh->setHasMetalnessInImage(hasMetalnessMap);
        m_resultMesh->setHasRoughnessInImage(hasRoughnessMap);
        m_resultMesh->setHasAmbientOcclusionInImage(hasAmbientOcclusionMap);
    }
    auto createResultEndTime = countTimeConsumed.elapsed();
    
    qDebug() << "The texture[" << TextureGenerator::m_textureSize << "x" << TextureGenerator::m_textureSize << "] generation took" << countTimeConsumed.elapsed() << "milliseconds";
}

 

drawSolubility这里也是一个Lambda Function,用于绘制溶解性贴图。之后是一个debug环节,遍历m_object的triangles.size(),如果size不为3,说明存在N-Gon,前面的三角化环节没有处理正确,借助Qt抛出异常,跳过这个三角形执行后面的操作。之后在处理局部UV时如果不能判断找到一个UV矩形,同样会抛出异常并跳过当前出问题的partUvRects。处理完毕后,填充邻域的半边结构,做一些最后的清理工作,完成纹理的生成任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值