本页内容

Qt Quick 3D - 天空材质示例

演示了如何使用SkyMaterial 实现过程化天空渲染和基于图像的照明。

采用PBR球体反射环境的光照程序化大气天空

本示例展示了如何使用SkyMaterial 渲染过程化天空,并将生成的立方贴图用作场景中PBR材质的基于图像的照明(IBL)的光探针。

AdvancedSky 辅助组件是SkyMaterial 的子类,它实现了一种基于物理的大气散射模型,该模型遵循 Hillaire 的《一种可扩展且适用于制作的天空与大气渲染技术》(2020),支持配置气溶胶、臭氧、视点高度以及基于光线追踪的体积云。

场景设置

该应用程序使用ApplicationWindow ,并以View3D 填充窗口。设置面板覆盖在左侧边缘,点击切换按钮时会滑入或滑出。相关的QtQuick3D 导入内容如下:

import QtQuick
import QtQuick.Controls

import QtQuick3D
import QtQuick3D.Helpers

定义天空材质

AdvancedSky 该材质在View3D 内部声明,因此与skyLight 共享同一Node (即太阳):

AdvancedSky {
    id: advancedSky
    skyLight: sun
}

AdvancedSky 是定义在AdvancedSky.qml 中的SkyMaterial 子类。其QML属性会自动作为uniform变量传递给片段着色器。

基于图像的照明

将天空材质分配给SceneEnvironment::skyMaterial 时,会将程序渲染的立方体贴图同时用作可见背景,以及场景中 PBR 材质的基于图像的照明光源:

environment: SceneEnvironment {
    backgroundMode: SceneEnvironment.SkyMaterial
    skyMaterial: advancedSky
    probeExposure: 0.25
    tonemapMode: SceneEnvironment.TonemapModeAces
}

演示中的最光滑球体几乎完美地反射了过程生成天空;而表面较粗糙的球体则显示出逐渐模糊的反射效果,这是由SkyMaterial 对过程生成立方贴图应用的 GGX 预滤波链所产生的。

选择天空的渲染方式

skyboxMode 该选项决定SkyMaterial 为可见背景生成的内容,与用于反射的 IBL 立方体独立:

  • SkyMaterial.Cubemap 该选项会采样辐射度立方贴图(即基于图像的照明所用的同一立方贴图),因此对于静态天空,立方贴图仅需渲染一次,之后每帧只需低成本采样。背景锐度受radianceMapSize 的限制。当仅摄像机移动时,这是每帧开销最低的方案。
  • ScreenSpace 模式每帧直接在屏幕上评估天空着色器——每轴分辨率为全分辨率、半分辨率或四分之一分辨率。它们将可见背景与IBL立方体解耦,在动态条件下(如移动的太阳、动画体素云)提供清晰的天空,但代价是每帧都需要渲染。较低的缩放比例以锐度换取速度。

在设置面板中切换“天空盒模式”下拉菜单,即可实时比较各种选项:立方贴图模式虽然计算成本较低,但在太阳和云层移动时画面会变模糊;而屏幕空间模式则能清晰追踪每一处变化。

帧预算管理

iblRenderFrames 将IBL预滤波器分布在多个帧中:相同的总iblSampleCount 值会以增量方式进行积分,每帧处理一个切片,直至收敛。默认值0 会在单帧内同时执行采样累积、归一化和辐照度计算。将该值设得更高可摊销预滤波器的开销,在IBL采样数量较高时保持场景响应灵敏。

太阳动画

当“动画太阳”开关开启时,FrameAnimation 会驱动太阳在地平线上来回移动。太阳位置的每次变化都会使IBL累加器失效,但只要将iblRenderFrames 设置得当,预滤波开销就会分散到各个帧中,从而避免任何单帧承担全部开销。

FrameAnimation {
    id: sunAnimator
    running: settingsPane.animateSun

    property real t: 0
    property real lastT: 0
    property int direction: 1

    onTriggered: {
        const min = -180
        const max = 0
        lastT = t
        t += direction * frameTime * settingsPane.sunSweepSpeed
        sun.eulerRotation.x = min + (Math.sin(t) * 0.5 + 0.5) * (max - min)
    }

    function updatePhaseFromSun() {
        const min = -180
        const max = 0
        let norm = Math.max(0.0, Math.min(1.0, (sun.eulerRotation.x - min) / (max - min)))
        t = Math.asin(norm * 2.0 - 1.0)
        direction = lastT < t ? 1 : -1
    }
}

设置面板

设置面板会滑动覆盖View3D ,可通过视口左上角的按钮进行切换。该面板分为两组。

天空

用于控制天空外观和云层。

  • 动画太阳— 在地平线上运行来回扫过的太阳动画。
  • 循环速度— 太阳动画的扫过速度。
  • 太阳高度角— 手动设置的太阳位置(以度为单位,0° = 黎明,90° = 正午,180° = 黄昏)。
  • 启用云层— 切换基于光线追踪的体积云。
  • 云覆盖率— 云层覆盖天空的比例(0 = 晴空,1 = 阴天)。
  • 动画风— 每帧漂移云纹理,持续清空 IBL 累加器 — 这是对帧预算控制的有用压力测试。
渲染

这些控件直接映射到SkyMaterial 基类的属性。

相机支持标准的WasdController 导航方式——使用 W/A/S/D 键配合鼠标拖拽进行环视。点击视口可将其设为键盘焦点。

示例项目 @ code.qt.io

另请参阅 SkyMaterial 和SceneEnvironment 。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.