如何镜像 Qt3D 中的坐标系?
我使用略微修改加载了全景图(能够使用元信息(倾斜和标题值来调整初始视图)并从自定义中解压缩单个面格式*.360
(只有六个*.jpg
- s))SkyboxEntity
。突然间,所有的纹理信息都被错误地反映出来了。
Scene3D {
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
multisample: true
entity: Entity {
Camera {
id: basicCamera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 60
nearPlane: 0.1
farPlane: 2.0 * Math.SQRT2
position: Qt.vector3d(0.0, 0.0, 0.0)
upVector: Qt.vector3d(0.0, 1.0, 0.0)
viewCenter: Qt.vector3d(1.0, 0.0, 0.0)
}
FirstPersonCameraController {
camera: basicCamera
lookSpeed: -200.0
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: basicCamera
clearColor: "transparent"
}
},
InputSettings {}
]
PanoEntity {
source: "file:///home/user/library.360"
}
}
}
panoentity.hpp
:
#pragma once
#include <Qt3DExtras>
class PanoEntity
: public Qt3DCore::QEntity
{
Q_OBJECT
Q_PROPERTY(QUrl source MEMBER source NOTIFY sourceChanged)
public :
explicit PanoEntity(Qt3DCore::QNode * const parent = Q_NULLPTR);
private Q_SLOTS :
void reloadTexture(QUrl source);
Q_SIGNALS :
void sourceChanged(const QUrl & path);
private :
Qt3DRender::QEffect * const effect = ::new Qt3DRender::QEffect;
Qt3DRender::QMaterial * const material = ::new Qt3DRender::QMaterial;
Qt3DRender::QTextureCubeMap * const texture = ::new Qt3DRender::QTextureCubeMap;
Qt3DRender::QTextureLoader * const textureLoader = ::new Qt3DRender::QTextureLoader;
Qt3DRender::QShaderProgram * const shader = ::new Qt3DRender::QShaderProgram;
Qt3DRender::QTechnique * const techinque = ::new Qt3DRender::QTechnique;
Qt3DRender::QFilterKey * const filterKey = ::new Qt3DRender::QFilterKey;
Qt3DRender::QRenderPass * const renderPass = ::new Qt3DRender::QRenderPass;
Qt3DExtras::QCuboidMesh * const mesh = ::new Qt3DExtras::QCuboidMesh;
Qt3DRender::QParameter * const textureParameter = ::new Qt3DRender::QParameter{QStringLiteral("skyboxTexture"), texture};
Qt3DRender::QTextureImage * const posXImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const posYImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const posZImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const negXImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const negYImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const negZImage = ::new Qt3DRender::QTextureImage;
Qt3DCore::QTransform * const transform = ::new Qt3DCore::QTransform;
QUrl source;
};
panoentity.cpp
:
#include "panoentity.h"
#include "configurator.hpp"
#include "pano.h"
#include "common.hpp"
#include "utility.hpp"
using namespace Qt3DCore;
using namespace Qt3DExtras;
using namespace Qt3DRender;
PanoEntity::PanoEntity(QNode * const parent)
: QEntity{parent}
{
textureLoader->setGenerateMipMaps(false);
shader->setVertexShaderCode(QShaderProgram::loadSource(QStringLiteral("qrc:/shaders/pano.vert")));
shader->setFragmentShaderCode(QShaderProgram::loadSource(QStringLiteral("qrc:/shaders/pano.frag")));
filterKey->setParent(effect);
filterKey->setName(QStringLiteral("renderingStyle"));
filterKey->setValue(QStringLiteral("forward"));
techinque->addFilterKey(filterKey);
renderPass->setShaderProgram(shader);
const auto cullFront = ::new QCullFace;
cullFront->setMode(QCullFace::Front);
const auto depthTest = ::new QDepthTest;
depthTest->setDepthFunction(QDepthTest::LessOrEqual);
renderPass->addRenderState(cullFront);
renderPass->addRenderState(depthTest);
techinque->addRenderPass(renderPass);
effect->addTechnique(techinque);
material->setEffect(effect);
material->addParameter(textureParameter);
mesh->setXYMeshResolution({2, 2});
mesh->setXZMeshResolution({2, 2});
mesh->setYZMeshResolution({2, 2});
posXImage->setFace(QTextureCubeMap::CubeMapPositiveX);
posXImage->setMirrored(false);
posYImage->setFace(QTextureCubeMap::CubeMapPositiveY);
posYImage->setMirrored(false);
posZImage->setFace(QTextureCubeMap::CubeMapPositiveZ);
posZImage->setMirrored(false);
negXImage->setFace(QTextureCubeMap::CubeMapNegativeX);
negXImage->setMirrored(false);
negYImage->setFace(QTextureCubeMap::CubeMapNegativeY);
negYImage->setMirrored(false);
negZImage->setFace(QTextureCubeMap::CubeMapNegativeZ);
negZImage->setMirrored(false);
transform->setRotationY(90.0f);
transform->setRotationX(90.0f);
texture->setMagnificationFilter(QTextureCubeMap::Linear);
texture->setMinificationFilter(QTextureCubeMap::Linear);
texture->setGenerateMipMaps(false);
texture->setWrapMode(QTextureWrapMode{QTextureWrapMode::ClampToEdge});
texture->addTextureImage(posXImage);
texture->addTextureImage(posYImage);
texture->addTextureImage(posZImage);
texture->addTextureImage(negXImage);
texture->addTextureImage(negYImage);
texture->addTextureImage(negZImage);
addComponent(mesh);
addComponent(material);
addComponent(transform);
connect(this, &PanoEntity::sourceChanged, this, &PanoEntity::reloadTexture);
}
void PanoEntity::reloadTexture(QUrl source)
{
if (!source.isLocalFile()) {
return;
}
QDir currentCatalog = Configurator::currentCatalogPath();
QFileInfo fileInfo{currentCatalog, source.toLocalFile()};
if (fileInfo.suffix() != "360") {
return;
}
if (fileInfo.canonicalPath().startsWith(currentCatalog.canonicalPath())) {
return;
}
QFile panoFile{fileInfo.filePath()};
if (!panoFile.open(QFile::ReadOnly)) {
return;
}
QDataStream dataStream{&panoFile};
Pano pano;
if (!pano.readHeader(dataStream)) {
return;
}
if (!pano.readBody(dataStream)) {
return;
}
dataStream.setDevice(Q_NULLPTR);
QDir cacheDir{getBaseDirName() + "/var/cache/pano"};
if (!cacheDir.exists() && !cacheDir.mkpath(cacheDir.path())) {
return;
}
if (!panoFile.reset()) {
return;
}
QCryptographicHash hash{QCryptographicHash::Md5};
hash.addData(&panoFile);
const auto baseName = QString::fromUtf8(hash.result().toHex());
int i = 0;
for (const auto suffix : {"_posx", "_negx", "_posy", "_negy", "_posz", "_negz"}) {
const QString fileName = baseName + suffix;
if (!cacheDir.exists(fileName)) {
QSaveFile file{cacheDir.filePath(fileName)};
if (file.open(QFile::WriteOnly | QFile::Truncate)) {
Q_ASSERT(file.pos() == 0);
if (file.write(pano.frames[i].buffer())) {
if (!file.commit()) {
qWarning() << QStringLiteral("Unable to commit %1").arg(file.fileName());
} else {
qDebug().noquote()
<< QStringLiteral("QML URL: %1; suffix %2 is cached in file %3")
.arg(source.toString(), QString::fromLatin1(suffix), file.fileName());
}
}
} else {
qDebug() << file.errorString();
}
} else {
qDebug() << "file already exist: " << cacheDir.filePath(fileName);
}
++i;
}
const auto basePath = cacheDir.filePath(baseName);
posXImage->setSource(QUrl::fromLocalFile(basePath + "_posx"));
posYImage->setSource(QUrl::fromLocalFile(basePath + "_negy"));
posZImage->setSource(QUrl::fromLocalFile(basePath + "_posz"));
negXImage->setSource(QUrl::fromLocalFile(basePath + "_negx"));
negYImage->setSource(QUrl::fromLocalFile(basePath + "_posy"));
negZImage->setSource(QUrl::fromLocalFile(basePath + "_negz"));
textureParameter->setValue(QVariant::fromValue(texture));
transform->setRotationZ(transform->rotationZ() + pano.heading());
transform->setRotationX(transform->rotationX() + pano.tilt());
}
我是否可以通过为每个轴交换pos
和neg
纹理并使它们镜像来实现这一目标,或者我可以使用{以某种方式交换两个坐标系轴{1}} CameraLens
的东西?或者也许最好只修改着色器以不同的顺序对纹理坐标进行采样?