main.cpp
//演示在 Qt3D 中创建高级材料。 //此示例演示创建高级自定义材料。 //描述 //高级自定义材质示例展示了更复杂的着色器,并演示了使用 QtQuick //用户界面和动画控制着色器属性。 水是一个 3D 网格,在 Blender //中建模和 uv 映射,然后作为 .obj 文件带入 Scene 3D。 //用户可以控制的着色器属性在 advancedcustommaterial/ //WaterMaterial.qm 中定义 //控件 //纹理比例滑块 //将顶点着色器内的纹理坐标相乘。 控制水面上纹理的大小。 //texCoord = vertexTexCoord * texCoordScale; //纹理速度滑块 //在advancedcustommaterial/Water.qml 中设置动画然后传 //递给顶点着色器的纹理坐标的偏移值。 创建纹理在表面上滚动的效果。 //FP vec2 waveMovCoord = waveTexCoord; //waveMovCoord.x += offsetx; //waveMovCoord.y -= offsety; //FP vec4 wave = texture2D(waveTexture, waveMovCoord); //镜面反射 //将片段着色器内的镜面反射纹理值相乘。 使水反射。 //vec4 specular = vec4(specularTextureColor.a*specularity); //vec4 outputColor = phongFunction(ka, diffuse, specular, shininess, worldPosition, worldView, wNormal); //失真 //将片段着色器中波浪纹理的红色和蓝色通道中的偏移量相乘。 使表面纹理更加随机。 //通过waveTexture的r和b颜色摆动newCoord //FP vec2 newCoord = texCoord; //newCoord.x += wave.r * waveStrenght; //newCoord.y -= wave.b * waveStrenght; //2 个动画层的 normalTexture 与 newCoord 混合 //Normal amount //将片段着色器内的法线贴图值相乘。 控制水面上较小波浪的可见性。 //FP vec3 tNormal = normalAmount * texture2D( normalTexture, movtexCoord+newCoord ).rgb - vec3( 1.0 )+(normalAmount * texture2D( normalTexture, multexCoord+newCoord ).rgb - vec3( 1.0 )); //波速 //修改顶点着色器内正弦波的频率。 控制波浪的速度。 //计算动画顶点位置 //波速 //修改顶点着色器内正弦波的频率。 控制波浪的速度。 //FP float sinPos = (vertexPosition.z)+(vertexPosition.x); //FP float sinPos2 = (vertexPosition.y/2.0)+(vertexPosition.z); //FP vec3 vertMod = vec3(vertexPosition.x,vertexPosition.y,vertexPosition.z); //vertMod = vec3(vertMod.x+=sin(vertYpos*2.2-sinPos2)*waveheight, // vertMod.y=sin(vertYpos*2.2+sinPos)*waveheight, // vertMod.z-=sin(vertYpos*2.2-cos(sinPos2))*waveheight); //FP vec3 vertModCom = vec3(vertMod.x+=cos(vertYpos*2.2-cos(sinPos2))*waveheight, // vertMod.y=sin(vertYpos*2.2+cos(sinPos))*waveheight, // vertMod.z-=cos(vertYpos*2.2-cos(sinPos))*waveheight); //波高 //乘以顶点着色器内的顶点 Y 位置。 控制波浪的高度。 //见上面 //网格旋转 //在 advancedcustommaterial/Water.qml 中旋转水网格。 #include <QGuiApplication> #include <QQuickView> #include <QOpenGLContext> #include <Qt3DRender/qt3drender-config.h> void setSurfaceFormat() { QSurfaceFormat format; //QSurfaceFormat 类表示 QSurface 的格式 //格式包括颜色缓冲区的大小,红色、绿色和蓝色; alpha 缓冲区的大小; 深度和模板缓冲区的大小; //以及用于多重采样的每个像素的样本数。 此外,该格式还包含表面配置参数,例如用于渲染的 OpenGL //配置文件和版本、是否启用立体缓冲区以及交换行为。 #if QT_CONFIG(opengles2) format.setRenderableType(QSurfaceFormat::OpenGLES); //设置所需的可渲染类型。 //在桌面 OpenGL、OpenGL ES 和 OpenVG 之间进行选择。 #else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { format.setVersion(4, 3); format.setProfile(QSurfaceFormat::CoreProfile); } //该枚举定义了底层 OpenGL 实现的类型。 //QOpenGLContext::LibGL 0 OpenGL //QOpenGLContext::LibGLES 1 OpenGL ES 2.0 or higher #endif //将最小深度缓冲区大小设置为 24 format.setDepthBufferSize(24); //将启用多重采样时每像素的首选采样数设置为 4 默认情况下,多重采样处于禁用状态。 format.setSamples(4); //将首选模板缓冲区大小设置为8位。 format.setStencilBufferSize(8); //设置全局默认表面格式。 //这种格式在 QOpenGLContext、QWindow、QOpenGLWidget 和类似的类中默认使用。 //通过使用相关类自己的 setFormat() 函数,它始终可以在每个实例的基础上被覆盖。 //但是,在应用程序启动时为所有窗口设置一次格式通常更方便。 在需要共享上下文的情况下,它还保证了 //正确的行为,因为通过此函数设置格式可保证所有上下文和表面,即使是由 Qt //内部创建的上下文和表面,也将使用相同的格式。 //注意:在设置 Qt::AA_ShareOpenGLContexts 时,强烈建议在构造 QGuiApplication 或 //QApplication 之前调用此函数。 //否则格式将不会应用于全局共享上下文,因此之后的上下文共享可能会出现问题。 QSurfaceFormat::setDefaultFormat(format); #if !QT_CONFIG(qt3d_rhi_renderer) qputenv("QSG_RHI_BACKEND", "opengl"); //此函数设置名为 varName 的环境变量的值。 如果变量不存在, //它将创建变量。 如果无法设置变量,则返回 0。 //使用空值调用 qputenv 会删除 Windows 上的环境变量,并使其在 Unix 上设置(但为空)。 //更喜欢使用 qunsetenv() 来实现完全可移植的行为。 #endif } int main(int argc, char **argv) { QGuiApplication app(argc, argv); setSurfaceFormat(); QQuickView view; //QQuickView 类提供了一个显示 Qt Quick 用户界面的窗口 view.resize(1920, 1080); //设置窗口大小 view.setResizeMode(QQuickView::SizeRootObjectToView); //此枚举指定如何调整视图大小。 //QQuickView::SizeViewToRootObject 0 视图会随着 QML 中的根项调整大小。 //QQuickView::SizeRootObjectToView 1 视图会自动将根项调整为视图的大小 view.setSource(QUrl("qrc:/main.qml")); //将源设置为 url,加载 QML 组件并实例化它。 //确保提供的 URL 完整且正确,特别是在从本地文件系统加载文件时使用 QUrl::fromLocalFile()。 //使用相同的 url 多次调用此方法将导致重新实例化 QML 组件。 view.show(); return app.exec(); }
main.qml
import QtQuick 2.0 //Qt Quick 模块是用于编写 QML 应用程序的标准库。 //Qt QML 模块提供 QML 引擎和语言基础设施,Qt Quick 模块提供使用 QML 创建用户界面所需 //的所有基本类型。它提供了一个可视化画布,包括用于创建和动画可视化组件、接收用户输入、创 //建数据模型和视图以及延迟对象实例化的类型。 //Qt Quick 模块提供了一个 QML API,该 API 提供了用于使用 QML 语言创建用户界面的 //QML 类型,以及一个用于使用 C++ 代码扩展 QML 应用程序的 C++ API。 //注意:一组基于 Qt Quick 的 UI 控件也可用于创建用户界面。有关更多信息,请参阅 Qt 快速控件。 import QtQuick.Scene3D 2.0 //Scene3D 类型用于将 Qt3D 场景集成到 QtQuick 2 场景中 //Scene3D 类型将实体提供的 Qt3D 场景渲染到多采样帧缓冲对象中。 //此对象稍后被 blit 为非多重采样的 Framebuffer 对象,然后使用预乘 alpha 进行渲染。 //如果不需要多重采样,可以通过将 multisample 属性设置为 false 来避免。 //在这种情况下,Scene3D 将直接渲染到非多重采样的 Framebuffer 对象中。 //如果要渲染的场景包含非不透明材质,您可能需要使用自定义混合参数修改这些材质,以便正确渲 //染它们。 例如,如果使用 PhongAlphaMaterial 和不透明透明颜色的场景,您可能需要添加: import Qt3D.Render 2.0 //Qt3DRender::Render //用于访问类 Renderer 和 QRenderPlugin 的命名空间 import QtQuick.Controls 2.0 //一个基本的可视化 QML 类型 //Item 类型是 Qt Quick 中所有可视项的基本类型。 //Qt Quick 中的所有可视项都继承自 Item。 尽管 Item //对象没有视觉外观,但它定义了所有视觉项的通用属性,例如 x 和 y //位置、宽度和高度、锚定和关键处理支持。 Item { //绘制一个带有可选边框的填充矩形 //矩形项目用于用纯色或渐变填充区域,和/或提供矩形边框。 Rectangle { //每个 Rectangle 项目都使用纯填充颜色(使用 color 属性指定)或渐变(使用 //Gradient 类型定义并使用渐变属性设置)绘制。 //如果同时指定了颜色和渐变,则使用渐变。 //您可以通过设置 border.color 和 border.width 属性为具有自己颜 //色和厚度的矩形添加可选边框。 将颜色设置为“透明”以绘制没有填充颜色的边框。 //您还可以使用 radius 属性创建圆角矩形。 //由于这会在矩形的角上引入弯曲边缘,因此设置 Item::antialiasing //属性以改善其外观可能是合适的。 id: scene property bool colorChange: true anchors.fill: parent color: "#2d2d2d" //旋转 QML 类型 //提供一种旋转项目的方法 //Rotation 类型提供了一种通过旋转类型变换来旋转 Item 的方法 //它允许(z 轴)相对于任意点旋转,并且还提供了一种为项目指定类似 3D //旋转的方法。 与旋转属性相比,这可以更好地控制项目旋转。 //对于类似 3D 的项目旋转,除了原点之外,您还必须指定旋转轴。 以下示例显示了应用于图像的各种类似 3D 的旋转。 //要绕其旋转的轴。 对于围绕点的简单 (2D) 旋转,您不需要指定轴,因为默认轴是 z 轴(axis { x: 0; y: 0; z: 1 })。 对于典型的 3D 类旋转,您通常会同时指定原点和轴。 transform: Rotation { id: sceneRotation axis.x: 1 axis.y: 0 axis.z: 0 origin.x: scene.width / 2 origin.y: scene.height / 2 } Rectangle { id: controlsbg anchors.fill: parent anchors.leftMargin: 10 anchors.topMargin: 10 anchors.rightMargin: 1720 anchors.bottomMargin: 10 color: "grey" //将其子项放置在列中 //Column 是一种将其子项沿单个列定位的类型。 它可以用作在不使用锚点的情况下垂直定位一系列项目的便捷方式。 Column { anchors.fill: parent anchors.leftMargin: 5 anchors.topMargin: 5 //间距是相邻项之间留空的像素量。 默认间距为 0。 spacing: 10 //8组标签和滑动条 Rectangle { id: slidertexscale width: 180 height: 60 color: "#2d2d2d" //指定如何将格式化文本添加到场景中 Text { id: scaletext anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top //anchors.margins 属性可用于一次将所有不同的边距设置为相同的值。 //它不会覆盖先前设置的特定边距; anchors.topMargin: 10 //要显示的文本。 Text 支持纯文本和富文本字符串。 text: "TEXTURE SCALE" //文本颜色。 //使用十六进制表示法定义的绿色文本示例: //color: "#00FF00" //使用 SVG 颜色名称定义的钢蓝色文本示例 //color: "steelblue" color: "white" font.bold: true font.pointSize: 12 } //用于通过沿轨道滑动手柄来选择值 Slider { id: slider1 anchors.fill: parent anchors.topMargin: 30 anchors.rightMargin: 10 anchors.leftMargin: 10 //此属性保存 from - to 范围内的值。 默认值为 0.0。 value: 1.0 //此属性保存范围的起始值。 默认值为 0.0。 from: 0.3 } } Rectangle { id: slidertexturespeed width: 180 height: 60 color: "#2d2d2d" Text { id: texturespeedtext anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 text: "TEXTURE SPEED" color: "white" font.bold: true font.pointSize: 12 } Slider { id: slider5 anchors.fill: parent anchors.topMargin: 30 anchors.rightMargin: 10 anchors.leftMargin: 10 value: 1.1 to: 4.0 from: 0.0 } } Rectangle { id: sliderspecularity width: 180 height: 60 color: "#2d2d2d" Text { id: specularitytext anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 text: "SPECULARITY" color: "white" font.bold: true font.pointSize: 12 } Slider { id: slider3 anchors.fill: parent anchors.topMargin: 30 anchors.rightMargin: 10 anchors.leftMargin: 10 value: 1.0 to: 3.0 from: 0.0 } } Rectangle { id: sliderdistortion width: 180 height: 60 color: "#2d2d2d" Text { id: distortiontext anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 text: "DISTORTION" color: "white" font.bold: true font.pointSize: 12 } Slider { id: slider7 anchors.fill: parent anchors.topMargin: 30 anchors.rightMargin: 10 anchors.leftMargin: 10 value: 0.015 to: 0.1 from: 0.0 } } Rectangle { id: slidernormal width: 180 height: 60 color: "#2d2d2d" Text { id: normaltext anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 text: "NORMAL AMOUNT" color: "white" font.bold: true font.pointSize: 12 } Slider { id: slider8 anchors.fill: parent anchors.topMargin: 30 anchors.rightMargin: 10 anchors.leftMargin: 10 value: 2.2 to: 4.0 from: 0.0 } } Rectangle { id: sliderwavespeed width: 180 height: 60 color: "#2d2d2d" Text { id: wawespeedtext anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 text: "WAVE SPEED" color: "white" font.bold: true font.pointSize: 12 } Slider { id: slider2 live: false anchors.fill: parent anchors.topMargin: 30 anchors.rightMargin: 10 anchors.leftMargin: 10 value: 0.75 to: 4.0 from: 0.1 } } Rectangle { id: sliderwaveheight width: 180 height: 60 color: "#2d2d2d" Text { id: waweheighttext anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 text: "WAVE HEIGHT" color: "white" font.bold: true font.pointSize: 12 } Slider { id: slider6 anchors.fill: parent anchors.topMargin: 30 anchors.rightMargin: 10 anchors.leftMargin: 10 value: 0.2 to: 0.5 from: 0.02 } } Rectangle { id: slidermeshrotation width: 180 height: 60 color: "#2d2d2d" Text { id: meshrotationtext anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 10 text: "MESH ROTATION" color: "white" font.bold: true font.pointSize: 12 } Slider { id: slider4 anchors.fill: parent anchors.topMargin: 30 anchors.rightMargin: 10 anchors.leftMargin: 10 value: 35.0 to: 360.0 from: 0.0 } } } } //Scene3D 类型用于将 Qt3D 场景集成到 QtQuick 2 场景中。 Scene3D { id: scene3d anchors.fill: parent anchors.leftMargin: 200 anchors.topMargin: 10 anchors.rightMargin: 10 anchors.bottomMargin: 10 focus: true //应为 3D 场景注册的方面列表。 //例如,如果场景使用 FrameAction,则“logic”方面应包含在列表中。 aspects: ["input", "logic"] //Scene3D.AutomaticAspectRatio 自动纵横比。 //Scene3D.UserAspectRatio用户定义的纵横比 cameraAspectRatioMode: Scene3D.AutomaticAspectRatio //引入SceneRoot.qml SceneRoot { id: root } } } }
SceneRoot.qml
import Qt3D.Core 2.0 import Qt3D.Render 2.0 import Qt3D.Extras 2.0 import Qt3D.Input 2.0 import QtQuick 2.0 as QQ2 //要显示的 3D 场景的根实体。 Entity { id: sceneRoot //定义将渲染场景的视点 Camera { id: camera //保存相机投影的类型。 默认值为 CameraLens.PerspectiveProjection。 projectionType: CameraLens.PerspectiveProjection //以度为单位保持相机的当前垂直视野。 //与 aspectRatio一起,此属性确定了相机可见的场景的多少 //在这方面,您可能会认为它类似于选择广角(宽水平视野)或长焦(窄水平视野) //镜头,具体取决于您想要捕捉多少场景。 fieldOfView 仅在projectionType 为 fieldOfView: 45 //将当前相机保持在相机平面附近。 比 NearPlane 更靠近相机的对象将不会被渲染。 nearPlane: 0.1 //持有当前相机远平面。 距离相机比 farPlane 更远的对象将不会被渲染。 farPlane: 1000.0 //相机在相对于父实体的坐标中的当前位置。 position: Qt.vector3d(0.0, 4.0, 15.0) //在相对于父实体的坐标中保持相机的当前向上向量。 //向上向量表示相机顶部面向的方向 想想拍照:在定位自己并将相机对准目标后,您可以向左或向右旋转相机,为您提供肖像或风景(或倾斜!)拍摄。 upVector 允许您控制这种类型的移动。 upVector: Qt.vector3d(0.0, 1.0, 0.0) //相机的当前视图中心,以相对于父实体的坐标表示。直观上,viewCenter 是相机指向的位置。 viewCenter: Qt.vector3d(0.0, -2.0, 0.0) } //FirstPersonCameraController 允许从第一人称视角控制场景相机 FirstPersonCameraController { camera: camera } //Entity 是一个 Node 子类,它可以聚合多个 Component3D 实例来指定其行为 Entity { //在 Qt 3D 场景中封装一个定向光对象 //定向光是一种行为类似于太阳光的光源。 来自定向光的光从同一方向以相同的强度照射所有对象,无论它们在场景中的哪个位置。 DirectionalLight { id: directional //指定平行光的方向。 worldDirection: Qt.vector3d(0.3, -1.0, 5.0).normalized(); //光颜色 color: "#fff2a3" //光强度。 intensity: 0.01 } Transform { id: lightpostransform translation: Qt.vector3d(0.0, 50.0, 60.0) } //Component3D 实例列表,定义实体的行为 components: [lightpostransform, directional] } Entity { //在 Qt 3D 场景中封装点光源对象 PointLight { id: pointL //光源颜色 //点光源是从一个点向所有方向发光的光源。 从概念上讲,这类似于标准灯泡发出的光。 //点光源使用三个衰减因子来描述光强度如何随距离降低。 这些因素被设计为在计算总衰减时一起使用。 对于 Qt3D Extras 中的材质,使用以下公式,其中距离是从灯光到被渲染表面的距离: // totalAttenuation = 1.0 / (constantAttenuation + (linearAttenuation * distance) + (quadraticAttenuation * distance * distance)); color: "#fff2a3" } Transform{ id: plightpostransform translation: Qt.vector3d(0.0, 4.0, 15.0) } components: [plightpostransform, pointL] } components: [ //RenderSettings 类型保存与渲染过程相关的设置并托管活动的 FrameGraph。 RenderSettings { //当前活动的 FrameGraph activeFrameGraph: ForwardRenderer { id: renderer clearColor: "black" camera: camera } }, InputSettings { } ] Water { } }
Water.qml
import Qt3D.Core 2.0 import Qt3D.Render 2.0 import Qt3D.Input 2.0 import Qt3D.Extras 2.0 import QtQuick 2.0 as QQ2 Entity { id: water WaterMaterial { id: watermaterial property real tox: 0.0 property real toy: 0.0 property real vertY: 1.0 property real waveRandomAnim: 0.0 diffuse: "qrc:/textures/WaterDiffuse.jpg" normal: "qrc:/textures/WaterNormal.jpg" specular: "qrc:/textures/WaterSpecular.jpg" wave: "qrc:/textures/Waterwave.jpg" sky: "qrc:/textures/sky.jpg" foam: "qrc:/textures/foam.jpg" textureScale: slider1.value wavescale: vertY * slider2.value specularity: slider3.value offsetx: tox * slider5.value offsety: toy * slider5.value normalAmount: slider8.value waveheight: slider6.value waveStrenght: slider7.value shininess: 100 waveRandom: waveRandomAnim } //自定义网格加载器。 //从各种格式的外部文件加载网格数据。 //在 Qt3D 5.9 中,Mesh 支持以下格式:Wavefront OBJ Stanford Triangle Format PLY STL(立体光刻) Mesh { id: watermesh source: "qrc:/models/waterPlane.obj" } Transform { id: waterTransform property real scale: 1.0 property real rotx: 0.0 scale3D: Qt.vector3d(scale, scale, scale) rotationY: slider4.value } Entity { id: waterEntity components: [watermesh, watermaterial, waterTransform] } //允许动画按顺序运行 //SequentialAnimation 和 ParallelAnimation 类型允许多个动画一起运行。 SequentialAnimation 中定义的动画一个接一个运行,而 ParallelAnimation 中定义的动画同时运行。 //Transition 中定义的动画自动并行运行,因此如果这是首选行为,SequentialAnimation 可用于将动画包含在 Transition 中。 //与任何其他动画类型一样,SequentialAnimation 可以通过多种方式应用,包括过渡、行为和属性值源。 Qt Quick 文档中的动画和过渡展示了多种创建动画的方法。 QQ2.SequentialAnimation { QQ2.NumberAnimation { target: watermaterial property: "waveRandomAnim" to: 3.0 duration: 4000 // easing.type: Easing.Linear } QQ2.NumberAnimation { target: watermaterial property: "waveRandomAnim" to: 1.0 duration: 4000 // easing.type: Easing.Linear } } QQ2.SequentialAnimation { running: true loops: QQ2.Animation.Infinite QQ2.ParallelAnimation { QQ2.NumberAnimation { target: watermaterial property: "toy" to: 10.0 duration: 100000 } QQ2.NumberAnimation { target: watermaterial property: "tox" to: 10.0 duration: 100000 } } QQ2.ParallelAnimation { QQ2.NumberAnimation { target: watermaterial property: "toy" to: 0.0 duration: 0 } QQ2.NumberAnimation { target: watermaterial property: "tox" to: 0.0 duration: 0 } } } QQ2.SequentialAnimation { running: true loops: QQ2.Animation.Infinite QQ2.NumberAnimation { target: watermaterial property: "vertY" to: 200 duration: 200000 // easing.type: Easing.Linear } QQ2.NumberAnimation { target: watermaterial property: "vertY" to: 2 duration: 200000 // easing.type: Easing.Linear } } }
WaterMaterial.qml
import Qt3D.Core 2.0 import Qt3D.Render 2.0 Material { id: root property color ambient: Qt.rgba(0.15, 0.35, 0.50, 1.0) property alias diffuse: diffuseTextureImage.source property alias normal: normalTextureImage.source property alias wave: waveTextureImage.source property alias specular: specularTextureImage.source property alias sky: skyTextureImage.source property alias foam: foamTextureImage.source property color specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0) property real shininess: 150.0 property real textureScale: 1.0 property real offsetx: 0.0 property real offsety: 0.0 property real wavescale: 0.0 property real specularity: 1.0 property real waveheight: 0.1 property real waveStrenght: 0.1 property real normalAmount: 2.0 property real waveRandom: 1.0 //材质使用的参数列表材质使用的参数列表 //为名称和值对提供存储空间。 这映射到着色器uniform //详细说明 //Parameter 可以被 RenderPass、Technique、Effect、Material、TechniqueFilter、RenderPassFilter 引用。 在运行时,根据为渲染的给定步骤选择了哪个着色器,如果着色器包含名称与参数名称匹配的统一体,则参数中包含的值将被转换并上传。 //例子 /* Parameter { name:"diffuseColor" value:"blue" } //适用于以下 GLSL 统一着色器声明 //uniform vec4 diffuseColor //uniform vec3 diffuseColor //uniform vec2 diffuseColor //uniform float diffuseColor */ parameters: [ Parameter { name: "ka"; value: Qt.vector3d(root.ambient.r, root.ambient.g, root.ambient.b) }, Parameter { name: "foamTexture" //纹理 //Texture2D //具有 Target2D 目标格式的 AbstractTexture //QAbstractTexture 用于提供纹理的基类 value: Texture2D { id: foamTexture //保存纹理提供程序的缩小过滤器。 minificationFilter: Texture.LinearMipMapLinear //保存纹理提供者的放大过滤器。 magnificationFilter: Texture.Linear //保存纹理提供程序的环绕模式。 wrapMode { x: WrapMode.Repeat y: WrapMode.Repeat } //保存纹理提供者是否应自动生成 mipmap。 generateMipMaps: true //保持纹理提供者的最大各向异性。 maximumAnisotropy: 16.0 TextureImage { id: foamTextureImage } } }, Parameter { name: "skyTexture" value: Texture2D { id: skyTexture minificationFilter: Texture.LinearMipMapLinear magnificationFilter: Texture.Linear wrapMode { x: WrapMode.Repeat y: WrapMode.Repeat } generateMipMaps: true maximumAnisotropy: 16.0 TextureImage { id: skyTextureImage } } }, Parameter { name: "waveTexture" value: Texture2D { id: waveTexture minificationFilter: Texture.LinearMipMapLinear magnificationFilter: Texture.Linear wrapMode { x: WrapMode.Repeat y: WrapMode.Repeat } generateMipMaps: true maximumAnisotropy: 16.0 TextureImage { id: waveTextureImage } } }, Parameter { name: "specularTexture" value: Texture2D { id: specularTexture minificationFilter: Texture.LinearMipMapLinear magnificationFilter: Texture.Linear wrapMode { x: WrapMode.Repeat y: WrapMode.Repeat } generateMipMaps: true maximumAnisotropy: 16.0 //封装从图像源创建 OpenGL 纹理图像所需的信息 TextureImage { id: specularTextureImage } } }, Parameter { name: "diffuseTexture" value: Texture2D { id: diffuseTexture minificationFilter: Texture.LinearMipMapLinear magnificationFilter: Texture.Linear wrapMode { x: WrapMode.Repeat y: WrapMode.Repeat } generateMipMaps: true maximumAnisotropy: 16.0 TextureImage { id: diffuseTextureImage } } }, Parameter { name: "normalTexture" value: Texture2D { id: normalTexture minificationFilter: Texture.LinearMipMapLinear magnificationFilter: Texture.Linear wrapMode { x: WrapMode.Repeat y: WrapMode.Repeat } generateMipMaps: true maximumAnisotropy: 16.0 TextureImage { id: normalTextureImage } } }, Parameter { name: "specularColor" value: Qt.vector3d(root.specularColor.r, root.specularColor.g, root.specularColor.b) }, Parameter { name: "shininess"; value: root.shininess }, Parameter { name: "texCoordScale"; value: textureScale }, Parameter { name: "offsetx"; value: root.offsetx }, Parameter { name: "offsety"; value: root.offsety }, Parameter { name: "vertYpos"; value: root.wavescale }, Parameter { name: "specularity"; value: root.specularity }, Parameter { name: "waveheight"; value: root.waveheight }, Parameter { name: "waveStrenght"; value: root.waveStrenght }, Parameter { name: "waveRandom"; value: root.waveRandom }, Parameter { name: "normalAmount"; value: root.normalAmount } ] //Effect Qt 3D 场景中效果的基类 //效果类型结合了一组技术和这些技术使用的参数来为材质产生渲染效果。 //一个 Effect 实例应该在可能的情况下在多个 Material 实例之间共享 //在 Effect 上定义的参数会覆盖在 Technique 和 RenderPass 中定义的参数(同名),但会被 RenderPassFilter、TechniqueFilter 和 Material 中的参数覆盖。 effect: Effect { property string vertex: "qrc:/shaders/gl3/water.vert" property string fragment: "qrc:/shaders/gl3/water.frag" property string vertexES: "qrc:/shaders/es2/water.vert" property string fragmentES: "qrc:/shaders/es2/water.frag" property string vertexRHI: "qrc:/shaders/rhi/water.vert" property string fragmentRHI: "qrc:/shaders/rhi/water.frag" //存储过滤键及其值 //FilterKey 是过滤器键值对的存储类型。 Technique 和 RenderPass 使用过滤器键来指定在渲染技术或渲染通道的哪个阶段使用。 //注意:FilterKey 节点不能被禁用。 FilterKey { id: forward //过滤键的名称 name: "renderingStyle" //过滤键的值 value: "forward" } //封装一个着色器程序 //ShaderProgram 类封装了一个着色器程序。 一个着色器程序由几个不同的着色器组成,例如顶点着色器和片段着色器。 //如果在着色器自省阶段遇到它们,Qt3D 将自动填充一组默认 uniforms。 ShaderProgram { id: gl3Shader //vertexShaderCode保存此着色器程序使用的顶点着色器代码 //loadSource返回从 sourceUrl 加载的着色器代码。 vertexShaderCode: loadSource(parent.vertex) fragmentShaderCode: loadSource(parent.fragment) } ShaderProgram { id: esShader vertexShaderCode: loadSource(parent.vertexES) fragmentShaderCode: loadSource(parent.fragmentES) } ShaderProgram { id: rhiShader vertexShaderCode: loadSource(parent.vertexRHI) fragmentShaderCode: loadSource(parent.fragmentRHI) } //启用 alpha-to-coverage 多重采样模式 //AlphaCoverage 类型启用 alpha-to-coverage 多重采样模式。 启用后,片段 alpha //值用作样本的覆盖率并与片段覆盖率值结合使用。 如果禁用多重采样,AlphaCoverage 将不执行任何操作。 //当需要与顺序无关的混合时,Alpha-to-coverage 最有用,例如在渲染树叶、草和其他丰富的植被时。 AlphaCoverage { id: alphaCoverage } //DepthTest 类型根据正在写入的样本的深度测试片段着色器的深度值 //深度测试允许在深度测试通过时写入片段颜色值,并拒绝测试失败的片段。 深度测试使用深度函数来测试片段深度值到 //z-buffer 的值。 如果底层表面没有 z 缓冲区,则 DepthTest 什么也不做。 DepthTest { id: depth //保存深度测试使用的当前函数。 默认为 DepthTest.Never depthFunction: DepthTest.Less } //封装技术 //Technique 指定了一组 RenderPass 对象、FilterKey 对象、Parameter 对象和一个 GraphicsApiFilter, //它们共同定义了给定图形 API 可以呈现的呈现技术。TechniqueFilter 使用过滤器键来选择 FrameGraph 特定部分的特定技术。 //在 Technique 上定义的参数会覆盖 RenderPass 中定义的参数(同名),但会被 RenderPassFilter、TechniqueFilter、Material 和 Effect 中的参数覆盖。 //在创建以图形 API 的多个版本为目标的 Effect 时,创建多个 Technique 节点很有用,每个节点都设置了 graphicsApiFilter 以匹配目标版本之一。 //在运行时,Qt3D 渲染器将根据支持的图形 API 版本和(如果指定)满足 FrameGraph 中给定 TechniqueFilter 的 FilterKey 节点来选择最合适的 Technique techniques: [ // OpenGL 3.1 Technique { //指定启用此技术的过滤器键列表 filterKeys: [ forward ] //指定正在使用的图形 API 过滤器 graphicsApiFilter { //对于 OpenGL,标识附加技术所需的 API。 //enum QGraphicsApiFilter::Api api: GraphicsApiFilter.OpenGL //此枚举标识所需的配置文件类型 profile: GraphicsApiFilter.CoreProfile majorVersion: 3 minorVersion: 1 } //RenderPass 指定单个渲染通道 - 着色器程序执行的实例 - 由 Technique 使用。 渲染通道由一个 ShaderProgram 和一个 FilterKey 对象列表、一个 RenderState 对象列表和一个 Parameter 对象列表组成。 //当至少一个被引用的 FilterKey 节点与 RenderPassFilter 中的任何 FilterKey 节点匹配或当 FrameGraph 中不存在 RenderPassFilter 时,RenderPass 使用给定的 RenderState 和 Parameter 节点执行 ShaderProgram。 //如果 RenderPass 定义了一个 Parameter,并且它在运行时存在于与该 pass 相关联的 Technique、Effect、Material、TechniqueFilter、RenderPassFilter 中的任何一个中,则它将被具有相同名称的 Parameter 覆盖。 这对于定义合理的默认值仍然很有用。 //在渲染时,对于 FrameGraph 的每个叶节点,通过累积由 FrameGraph 分支中的所有 RenderStateSet 节点定义的状态来记录基本渲染状态。 每个 RenderPass 都可以通过指定自己的 RenderState 节点来重载此基本渲染状态。 renderPasses: RenderPass { shaderProgram: gl3Shader renderStates: [alphaCoverage ] } }, // OpenGLES 2.0 Technique { filterKeys: [ forward ] graphicsApiFilter { api: GraphicsApiFilter.OpenGLES majorVersion: 2 minorVersion: 0 } renderPasses: RenderPass { shaderProgram: esShader renderStates: [ alphaCoverage ] } }, // OpenGL ES 2 Technique { filterKeys: [ forward ] graphicsApiFilter { api: GraphicsApiFilter.OpenGLES profile: GraphicsApiFilter.NoProfile majorVersion: 2 minorVersion: 0 } renderPasses: RenderPass { shaderProgram: esShader renderStates: [ alphaCoverage ] } }, // RHI Technique { filterKeys: [ forward ] graphicsApiFilter { api: GraphicsApiFilter.RHI profile: GraphicsApiFilter.NoProfile majorVersion: 1 minorVersion: 0 } renderPasses: RenderPass { shaderProgram: rhiShader renderStates: [ alphaCoverage ] } } ] } }
water.vert
#version 150 core in vec3 vertexPosition; in vec3 vertexNormal; in vec2 vertexTexCoord; in vec4 vertexTangent; out vec3 worldPosition; out vec3 worldNormal; out vec4 worldTangent; out vec2 texCoord; out vec2 movtexCoord; out vec2 multexCoord; out vec2 waveTexCoord; out vec2 skyTexCoord; out vec3 vpos; uniform mat4 modelMatrix; uniform mat3 modelNormalMatrix; uniform mat4 mvp; uniform float offsetx; uniform float offsety; uniform float vertYpos; uniform float texCoordScale; uniform float waveheight; uniform float waveRandom; void main() { // 为片段着色器缩放纹理坐标 //纹理比例滑块 //将顶点着色器内的纹理坐标相乘。 控制水面上纹理的大小。 texCoord = vertexTexCoord * texCoordScale; movtexCoord = vertexTexCoord * texCoordScale; multexCoord = vertexTexCoord * (texCoordScale*0.5); waveTexCoord = vertexTexCoord * (texCoordScale * 6); skyTexCoord = vertexTexCoord * (texCoordScale * 0.2); // 将动画 x 和 y 偏移添加到 SKY、MOV 和 MUL texCoords movtexCoord = vec2(texCoord.x+offsetx,texCoord.y+offsety); multexCoord = vec2(texCoord.x-offsetx,texCoord.y+offsety); skyTexCoord = vec2(texCoord.x-(offsetx/2),texCoord.y-(offsety/2)); // 将位置、法线和切线转换为世界坐标 worldPosition = vec3(modelMatrix * vec4(vertexPosition, 1.0)); worldNormal = normalize(modelNormalMatrix * vertexNormal); worldTangent.xyz = normalize(vec3(modelMatrix * vec4(vertexTangent.xyz, 0.0))); worldTangent.w = vertexTangent.w; // 计算动画顶点位置 float sinPos = (vertexPosition.z)+(vertexPosition.x); float sinPos2 = (vertexPosition.y/2)+(vertexPosition.z); vec3 vertMod = vec3(vertexPosition.x,vertexPosition.y,vertexPosition.z); vertMod = vec3(vertMod.x+=sin(vertYpos*2.2-sinPos2)*waveheight, vertMod.y=sin(vertYpos*2.2+sinPos)*waveheight, vertMod.z-=sin(vertYpos*2.2-cos(sinPos2))*waveheight); vec3 vertModCom = vec3(vertMod.x+=cos(vertYpos*2.2-cos(sinPos2))*waveheight, vertMod.y=sin(vertYpos*2.2+cos(sinPos))*waveheight, vertMod.z-=cos(vertYpos*2.2-cos(sinPos))*waveheight); // 仅向世界 pos.y 零上方的顶点添加波浪动画 if(vertexPosition.y < 0.0){vertModCom = vertexPosition;} else{vertModCom = vertModCom;} vpos = vertModCom; // 计算剪辑坐标中的顶点位置 gl_Position = mvp * vec4(vertModCom, 1.0); }
water.frag
#version 150 core in vec3 worldPosition; in vec3 worldNormal; in vec4 worldTangent; in vec2 texCoord; in vec2 waveTexCoord; in vec2 movtexCoord; in vec2 multexCoord; in vec2 skyTexCoord; in vec3 vpos; in vec3 color; uniform sampler2D diffuseTexture; uniform sampler2D specularTexture; uniform sampler2D normalTexture; uniform sampler2D waveTexture; uniform sampler2D skyTexture; uniform sampler2D foamTexture; uniform float offsetx; uniform float offsety; uniform float specularity; uniform float waveStrenght; uniform vec4 ka; uniform vec3 specularColor; uniform float shininess; uniform float normalAmount; uniform vec3 eyePosition; out vec4 fragColor; #pragma include phong.inc.frag #pragma include coordinatesystems.inc void main() { //移动 waveTexCoords //纹理速度滑块 //在advancedcustommaterial/Water.qml 中设置动画然后传 //递给顶点着色器的纹理坐标的偏移值。 创建纹理在表面上滚动的效果。 vec2 waveMovCoord = waveTexCoord; waveMovCoord.x += offsetx; waveMovCoord.y -= offsety; vec4 wave = texture(waveTexture, waveMovCoord); //失真 //将片段着色器中波浪纹理的红色和蓝色通道中的偏移量相乘。 使表面纹理更加随机。 //通过waveTexture的r和b颜色摆动newCoord vec2 newCoord = texCoord; newCoord.x += wave.r * waveStrenght; newCoord.y -= wave.b * waveStrenght; // 在插值的 texCoords 处采样纹理 // 使用默认的 texCoord 进行漫反射(它不会在 x 或 y 上移动,因此可以用作“水下地面”)。 vec4 diffuseTextureColor = texture(diffuseTexture, texCoord); // 2 个与 newCoord 混合的镜面纹理动画层 vec4 specularTextureColor = texture( specularTexture, multexCoord+newCoord) + (texture( specularTexture, movtexCoord+newCoord )); // 2 个动画层的 normalTexture 与 newCoord 混合 //Normal amount //将片段着色器内的法线贴图值相乘。 控制水面上较小波浪的可见性。 vec3 tNormal = normalAmount * texture( normalTexture, movtexCoord+newCoord ).rgb - vec3( 1.0 )+(normalAmount * texture( normalTexture, multexCoord+newCoord ).rgb - vec3( 1.0 )); // 动画天空纹理层 vec4 skycolor = texture(skyTexture, skyTexCoord); skycolor = skycolor * 0.4; //动画foamTexture层 vec4 foamTextureColor = texture(foamTexture, texCoord); mat3 tangentMatrix = calcWorldSpaceToTangentSpaceMatrix(worldNormal, worldTangent); mat3 invertTangentMatrix = transpose(tangentMatrix); vec3 wNormal = normalize(invertTangentMatrix * tNormal); vec3 worldView = normalize(eyePosition - worldPosition); vec4 diffuse = vec4(diffuseTextureColor.rgb, vpos.y); //将片段着色器内的镜面反射纹理值相乘。 使水反射。 vec4 specular = vec4(specularTextureColor.a*specularity); vec4 outputColor = phongFunction(ka, diffuse, specular, shininess, worldPosition, worldView, wNormal); outputColor += vec4(skycolor.rgb, vpos.y); outputColor += (foamTextureColor.rgba*vpos.y); fragColor = vec4(outputColor.rgb,1.0); }