Qt3D设置CullFace为FrontAndBack遇ActiveFrameGraph问题
解决Qt3D中非流形网格内外表面均显示纹理的问题
问题描述
我有一个带纹理的非流形网格,目前内部面显示为纯白色,希望内外表面都能显示纹理颜色。根据旧帖提示需要将CullFace设置为FrontAndBack,但尝试用RenderSettings、RenderStateSet配置时,不清楚组件关联方式和ActiveFrameGraph的绑定方法,调整后要么内部面还是白的,要么程序崩溃或提示缺少SurfaceSelector。
原始代码
from PyQt6.QtWidgets import QMainWindow, QApplication, QWidget, QHBoxLayout, QVBoxLayout from PyQt6.QtGui import QVector3D from PyQt6.QtCore import Qt import sys from PyQt6.Qt3DCore import QEntity, QTransform from PyQt6 import Qt3DRender from PyQt6 import QtCore from PyQt6.Qt3DInput import QInputAspect from PyQt6.Qt3DExtras import Qt3DWindow, QOrbitCameraController, QDiffuseMapMaterial class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.view = Qt3DWindow() self.container = QWidget.createWindowContainer(self.view) self.setCentralWidget(self.container) # Create & set layouts main_layout = QVBoxLayout() main_layout.addWidget(self.container) main_widget = QWidget() main_widget.setLayout(main_layout) self.setCentralWidget(main_widget) # Define object paths filepath1 = "some_path.obj" texture_filepath1 = "some_other_path.jpg" # Root entity self.rootEntity = QEntity() # Load mesh mesh_object = Qt3DRender.QMesh() mesh_object.setSource(QtCore.QUrl.fromLocalFile(filepath1)) mesh_entity = QEntity(self.rootEntity) mesh_entity.addComponent(mesh_object) # Load/create textures mesh_texture_image = Qt3DRender.QTextureImage(self.rootEntity) mesh_texture_image.setSource(QtCore.QUrl.fromLocalFile(texture_filepath1)) mesh_texture_image.setEnabled(True) mesh_texture_2D = Qt3DRender.QTexture2D(self.rootEntity) mesh_texture_2D.addTextureImage(mesh_texture_image) # Create Materials diffuse_material = QDiffuseMapMaterial(self.rootEntity) diffuse_material.setDiffuse(mesh_texture_2D) diffuse_material.setShininess(0) # Add material components to entity mesh_entity.addComponent(diffuse_material) # Cull face - 这里的配置有问题 render_settings = self.view.renderSettings() activeframegraph = render_settings.activeFrameGraph self.render_state_set = Qt3DRender.QRenderStateSet(render_settings) self.cull_face = Qt3DRender.QCullFace(self.render_state_set) self.cull_face.setMode(Qt3DRender.QCullFace.CullingMode.FrontAndBack) self.render_state_set.addRenderState(self.cull_face) #render_settings.setActiveFrameGraph(self.render_state_set) self.rootEntity.addComponent(render_settings) # Camera. camera = self.view.camera() camera.lens().setPerspectiveProjection(45.0, 16.0 / 9.0, 1, 1000.0) camera.setPosition(QVector3D(0.0, 0, 120.0)) camera.setViewCenter(QVector3D(0.0, 100.0, 0.0)) # For camera controls. camController = QOrbitCameraController(self.rootEntity) camController.setLinearSpeed(200.0) camController.setLookSpeed(280.0) camController.setCamera(camera) # Light light_entity = QEntity(self.rootEntity) light = Qt3DRender.QPointLight(light_entity) light.setConstantAttenuation(0) light_entity.addComponent(light) light_transform = QTransform() light_transform.setTranslation(QVector3D(350, 100, 200)) light_entity.addComponent(light_transform) light_entity_2 = QEntity(self.rootEntity) light_2 = Qt3DRender.QPointLight(light_entity_2) light_2.setConstantAttenuation(0) light_entity_2.addComponent(light_2) light_transform = QTransform() light_transform.setTranslation(QVector3D(-350, 100, 200)) light_entity_2.addComponent(light_transform) # Object picker self.object_picker = Qt3DRender.QObjectPicker(self.rootEntity) self.object_picker.pressed.connect(self.on_object_picked) self.rootEntity.addComponent(self.object_picker) self.pick_set = self.view.renderSettings().pickingSettings() self.pick_set.setPickMethod(Qt3DRender.QPickingSettings.PickMethod.PrimitivePicking) self.view.setRootEntity(self.rootEntity) def on_object_picked(self, pick_event): print("Object picked at:", pick_event.worldIntersection().x(), pick_event.worldIntersection().y(), pick_event.worldIntersection().z()) class Application(QMainWindow): def __init__(self): super().__init__() view3d = View3D() self.setCentralWidget(view3d) self.show() if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.setGeometry(100, 100, 800, 600) window.setWindowTitle("3D Object Viewer") window.show() sys.exit(app.exec())
解决方案
问题根源
Qt3DWindow默认已经构建了完整的帧图结构(包含QRenderSurfaceSelector、QForwardRenderer等必需节点),直接替换activeFrameGraph会破坏原有依赖导致崩溃;同时不要将RenderSettings添加到实体树,它是窗口的渲染配置组件,不属于实体节点。
修改后的代码
from PyQt6.QtWidgets import QMainWindow, QApplication, QWidget, QHBoxLayout, QVBoxLayout from PyQt6.QtGui import QVector3D from PyQt6.QtCore import Qt import sys from PyQt6.Qt3DCore import QEntity, QTransform from PyQt6 import Qt3DRender from PyQt6 import QtCore from PyQt6.Qt3DInput import QInputAspect from PyQt6.Qt3DExtras import Qt3DWindow, QOrbitCameraController, QDiffuseMapMaterial class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.view = Qt3DWindow() self.container = QWidget.createWindowContainer(self.view) self.setCentralWidget(self.container) # Create & set layouts main_layout = QVBoxLayout() main_layout.addWidget(self.container) main_widget = QWidget() main_widget.setLayout(main_layout) self.setCentralWidget(main_widget) # Define object paths filepath1 = "some_path.obj" texture_filepath1 = "some_other_path.jpg" # Root entity self.rootEntity = QEntity() # Load mesh mesh_object = Qt3DRender.QMesh() mesh_object.setSource(QtCore.QUrl.fromLocalFile(filepath1)) mesh_entity = QEntity(self.rootEntity) mesh_entity.addComponent(mesh_object) # Load/create textures mesh_texture_image = Qt3DRender.QTextureImage(self.rootEntity) mesh_texture_image.setSource(QtCore.QUrl.fromLocalFile(texture_filepath1)) mesh_texture_image.setEnabled(True) mesh_texture_2D = Qt3DRender.QTexture2D(self.rootEntity) mesh_texture_2D.addTextureImage(mesh_texture_image) # Create Materials diffuse_material = QDiffuseMapMaterial(self.rootEntity) diffuse_material.setDiffuse(mesh_texture_2D) diffuse_material.setShininess(0) # Add material components to entity mesh_entity.addComponent(diffuse_material) # -------------------------- # 修正后的CullFace配置 # -------------------------- render_settings = self.view.renderSettings() # 获取默认的帧图根节点 default_frame_graph = render_settings.activeFrameGraph() # 创建RenderStateSet作为默认帧图的子节点 self.render_state_set = Qt3DRender.QRenderStateSet(default_frame_graph) # 创建CullFace并设置模式 self.cull_face = Qt3DRender.QCullFace(self.render_state_set) self.cull_face.setMode(Qt3DRender.QCullFace.CullingMode.FrontAndBack) # 将CullFace添加到RenderStateSet self.render_state_set.addRenderState(self.cull_face) # -------------------------- # Camera. camera = self.view.camera() camera.lens().setPerspectiveProjection(45.0, 16.0 / 9.0, 1, 1000.0) camera.setPosition(QVector3D(0.0, 0, 120.0)) camera.setViewCenter(QVector3D(0.0, 100.0, 0.0)) # For camera controls. camController = QOrbitCameraController(self.rootEntity) camController.setLinearSpeed(200.0) camController.setLookSpeed(280.0) camController.setCamera(camera) # Light light_entity = QEntity(self.rootEntity) light = Qt3DRender.QPointLight(light_entity) light.setConstantAttenuation(0) light_entity.addComponent(light) light_transform = QTransform() light_transform.setTranslation(QVector3D(350, 100, 200)) light_entity.addComponent(light_transform) light_entity_2 = QEntity(self.rootEntity) light_2 = Qt3DRender.QPointLight(light_entity_2) light_2.setConstantAttenuation(0) light_entity_2.addComponent(light_2) light_transform = QTransform() light_transform.setTranslation(QVector3D(-350, 100, 200)) light_entity_2.addComponent(light_transform) # Object picker self.object_picker = Qt3DRender.QObjectPicker(self.rootEntity) self.object_picker.pressed.connect(self.on_object_picked) self.rootEntity.addComponent(self.object_picker) self.pick_set = self.view.renderSettings().pickingSettings() self.pick_set.setPickMethod(Qt3DRender.QPickingSettings.PickMethod.PrimitivePicking) self.view.setRootEntity(self.rootEntity) def on_object_picked(self, pick_event): print("Object picked at:", pick_event.worldIntersection().x(), pick_event.worldIntersection().y(), pick_event.worldIntersection().z()) if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.setGeometry(100, 100, 800, 600) window.setWindowTitle("3D Object Viewer") window.show() sys.exit(app.exec())
关键修改点
- 保留默认帧图结构:不替换
activeFrameGraph,而是在现有帧图节点下添加QRenderStateSet - 正确设置父节点:将
QRenderStateSet的父节点设为默认帧图根节点,确保渲染状态被正确应用 - 移除错误操作:删除
self.rootEntity.addComponent(render_settings),RenderSettings属于Qt3DWindow,不需要添加到实体树
内容的提问来源于stack exchange,提问作者MAX
相关产品推荐
相关产品推荐

