You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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())

关键修改点

  1. 保留默认帧图结构:不替换activeFrameGraph,而是在现有帧图节点下添加QRenderStateSet
  2. 正确设置父节点:将QRenderStateSet的父节点设为默认帧图根节点,确保渲染状态被正确应用
  3. 移除错误操作:删除self.rootEntity.addComponent(render_settings),RenderSettings属于Qt3DWindow,不需要添加到实体树

内容的提问来源于stack exchange,提问作者MAX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 06:12:08