main.cpp
#include#include #include int main(int argc, char* argv[]) { QGuiApplication app(argc, argv); #if !QT_CONFIG(qt3d_rhi_renderer) qputenv("QSG_RHI_BACKEND", "opengl"); #endif //QQuickView 类提供了一个用于显示 Qt Quick 用户界面的窗口 //这是 QQuickWindow 的一个方便的子类, //当给定主源文件的 URL 时,它将自动加载和显示 QML 场景。 //或者,您可以使用 QQmlComponent 实例化您自己的对象并将它们放置在手动设置的 QQuickWindow 中。 //典型用法: //要接收与使用 QQuickView 加载和执行 QML 相关的错误 //您可以连接到 statusChanged() 信号并监视 QQuickView::Error。 //错误可通过 QQuickView::errors() 获得。 //QQuickView 还管理视图和根对象的大小。 //默认情况下,resizeMode 是 SizeViewToRootObject, //它将加载组件并将其调整为视图的大小。 //或者,可以将 resizeMode 设置为 SizeRootObjectToView, //这会将视图大小调整为根对象的大小。 QQuickView view; view.resize(500, 500); view.setResizeMode(QQuickView::SizeRootObjectToView); view.setSource(QUrl("qrc:/main.qml")); view.show(); return app.exec(); }
main.qml
import QtQuick 2.0
import QtQuick.Scene3D 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.1
//一个基本的可视化 QML 类型
//Item 类型是 Qt Quick 中所有可视项的基本类型。
//Qt Quick 中的所有可视项都继承自 Item。
//尽管 Item 对象没有视觉外观,但它定义了所有视觉项的通用属性,
//例如 x 和 y 位置、宽度和高度、锚定和关键处理支持。
//Item 类型可用于将多个项目分组到单个根视觉项目下。 例如:
//事件处理
//所有基于 Item 的视觉类型都可以使用 Input Handlers 来处理传入的输入事件(QInputEvent 的子类)
//,例如鼠标、触摸和按键事件。 这是处理事件的首选声明方式。
//处理触摸事件的另一种方法是继承 QQuickItem,
//在构造函数中调用 setAcceptTouchEvents() 并覆盖 touchEvent()。
//接受整个事件以停止向下面的项目交付,并专门抓取所有事件的接触点。
//使用 QPointerEvent::setExclusiveGrabber() 仅抓取某些接触点,并允许进一步传递事件。
//同样,QQuickItem 子类可以调用 setAcceptedMouseButtons() 注册接收鼠标按钮事件
//setAcceptHoverEvents() 接收悬停事件(没有按下按钮时的鼠标移动),
//并覆盖虚拟函数 mousePressEvent()、mouseMoveEvent() 和 mouseReleaseEvent ()。
//那些也可以接受事件以防止进一步交付并同时获得隐式抓取;
//或显式获取 QMouseEvent 携带的单个 QEventPoint。
//通过 Keys 附加属性,所有基于 Item 的视觉类型都可以使用键处理。
//Keys 附加属性提供基本信号,例如按下和释放,以及特定键的信号,例如 spacePressed。
//下面的示例将键盘焦点分配给项目并通过通用 onPressed 处理程序处理左键,
//并通过 onReturnPressed 处理程序处理返回键:
//有关详细文档,请参阅 Keys 附加属性。
//布局镜像
//可以使用 LayoutMirroring 附加属性来镜像项目布局。
//这会导致锚点水平反转,也会导致布置或定位其子项(例如 ListView 或 Row)的项目水平反转其布局的方向。
//有关更多详细信息,请参阅 LayoutMirroring。
//项目层
//一个 Item 通常会直接渲染到它所属的窗口中
//。 但是,通过设置 layer.enabled,可以将项目及其整个子树委托到offscreen surface。
//只有offscreen surface,一个纹理,才会被绘制到窗口中。
//如果需要与项目的纹理大小不同的纹理大小,可以使用 layer.textureSize。
//要仅将项目的一部分渲染到纹理中,请使用 layer.sourceRect。
//也可以指定 layer.sourceRect 使其超出项目的边界。
//在这种情况下,外部将填充透明像素。
//如果 layer.smooth 设置为 true,
//该项目将使用线性插值进行缩放
//如果 layer.mipmap 设置为 true,则该项目将使用 mipmap 进行下采样。
//Mipmapping 可以提高缩小项目的视觉质量
//对于单个 Image 项的 mipmapping,首选 Image::mipmap。
//图层不透明度与项目不透明度
//将不透明度应用于项目层次结构时,不透明度将单独应用于每个项目。
//当不透明度应用于子树时,这可能会导致不希望的视觉结果。
//考虑以下示例:
//以根项目的不透明度为 1 渲染图层,然后在绘制时将根项目的不透明度应用于纹理。
//这意味着可以在大型项目层次结构中从透明淡化到不透明
//反之亦然,可以在没有正常项目逐项目 alpha 混合所具有的重叠伪影的情况下完成。
//这是启用图层的相同示例:
//Combined with ShaderEffects
//将 layer.enabled 设置为 true 会将项目转换为纹理提供程序
//,从而可以将项目直接用作纹理,例如与 ShaderEffect 类型结合使用。
//可以在运行时使用 layer.effect 对图层应用效果:
//有关使用效果的更多信息,请参见 ShaderEffect。
//注意: layer.enabled 实际上只是一种更方便的使用 ShaderEffectSource 的方式。
//内存和性能
//当项目的层被启用时,场景图将在 GPU 中分配等于宽 x 高 x 4 的内存。在内存受限的配置中,应谨慎使用大层。
//在 QPainter / QWidget 世界中,有时在像素图、图像或纹理中缓存复杂内容是有利的。
//在 Qt Quick 中,由于场景图渲染器已经应用了这些技术,因此在大多数情况下并非如此。
//由于批处理已经减少了过多的绘制调用,并且在大多数情况下,缓存最终会混合比原始内容更多的像素。
//因此,渲染到屏幕外的开销以及绘制结果纹理所涉及的混合通常比简单地让项目及其子项正常绘制成本更高。
//此外,在渲染过程中无法对使用图层的项目进行批处理。
//这意味着具有许多分层项目的场景可能存在性能问题。
//分层对于视觉效果来说非常方便和有用
//但在大多数情况下应该在效果的持续时间内启用并在之后禁用。
//Property documentation
//implicitHeight : real
//implicitWidth : real
//如果未指定宽度或高度,则定义项目的自然宽度或高度。
//大多数项目的默认隐式大小为 0x0,但是有些项目具有无法覆盖的固有隐式大小,例如,图像和文本。
//设置隐式大小对于根据内容定义具有首选大小的组件很有用,例如:
//注意:使用implicitWidth of Text 或TextEdit 并显式设置宽度会导致性能损失,因为文本必须布局两次
//height : real
//width : real
//x : real
//y : real
//定义项目的位置和大小。 默认值为 0。
//(x,y) 位置是相对于父级的。
//Item{x:100;y:100;width:100;height:100}
//children : list-
//resources : list
ComputeframeGraph.qml
import Qt3D.Core 2.0
import Qt3D.Render 2.9
//Qt3D 场景上的视口
Viewport {
property alias camera: selector.camera
//提供一种指定渲染表面的方法
//RenderSurfaceSelector 可用于选择表面,Qt3D 在此渲染内容。 表面可以是窗口表面或屏幕外表面。
//externalRenderTargetSize 用于在使用离屏表面时指定渲染目标的实际大小。
//当系统使用 DPI 缩放时,鼠标事件使用的逻辑表面大小和表面的实际“物理”大小可能不同。
//surfacePixelRatio 是将逻辑大小转换为物理大小的因子。
RenderSurfaceSelector {
id: surfaceSelector
// 清除缓冲区
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
NoDraw {}
}
// Compute Pass
//frameGraph 节点为 GPU 上的计算着色器发出工作
//DispatchCompute 允许为计算着色器发出工作以在 GPU 上运行。
//workGroupX、workGroupY 和 workGroupZ 属性指定计算着色器调用的工作组大小。
//ComputeCommand 组件需要添加到实体中,以指示 Qt3D 从实体中选择材料和几何体以进行计算调用。
//着色器调用的工作组大小将是 DispatchCompute 和 ComputeCommand 中指定的工作组大小的最大值。
DispatchCompute {
workGroupX: 50; workGroupY: 1; workGroupZ: 1
//一个 frameGraphNode 用于选择使用的技术。
//TechniqueFilter 指定 frameGraph 在呈现实体时使用哪些Technique。
//TechniqueFilter 指定了 FilterKey 对象和 Parameter 对象的列表。
//当 frameGraph 中存在 TechniqueFilter 时,仅使用与列表中的键匹配的技术进行渲染。
//列表中的参数可用于设置着色器参数的值。
//echniqueFilter 中的参数会覆盖 Material、Effect、Technique 和 RenderPass 中的参数,但会被 RenderPassFilter 中的参数覆盖。
TechniqueFilter {
matchAll: [
FilterKey { name: "type"; value: "compute"}
]
}
}
// 从 Compute Pass 中计算的缓冲区中绘制粒子
CameraSelector {
id: selector
TechniqueFilter {
//放置内存屏障的类。
//MemoryBarrier frameGraph 节点用于在渲染的特定时间放置特定的内存屏障。
//这是在 GPU 上正确同步绘图和计算命令所必需的。
//屏障定义了先前命令发出的内存操作的顺序。
//这意味着如果 command1 正在操作一个缓冲区,
//该缓冲区将在后面的 command2 中用作顶点属性缓冲区,
//那么内存屏障应该放在 command1 之后,并为顶点属性缓冲区设置适当的屏障类型。
//当在 frameGraph 分支中找到 QMemoryBarrier 节点时,
//障碍将在任何绘制或计算命令之前强制执行,即使这些在分支中定义得更深。
//对于 OpenGL 渲染,此页面提供了有关内存模型的更多信息
MemoryBarrier { waitFor: MemoryBarrier.VertexAttributeArray }
matchAll: [
FilterKey { name: "type"; value: "draw"}
]
}
}
}
}
ComputeMaterial.qml
import Qt3D.Core 2.0
import Qt3D.Render 2.0
//Material 提供了一种指定实体渲染的方法。
//任何方面都可以定义自己的 Material 子类型,以便 Material 可以用来描述视觉元素;
//例如,声音从元素反射的方式、表面温度等。
//就其本身而言,材质不做任何事情。
//只有当它引用一个 Effect 节点时,材质才会变得有用。
//在实践中,一个 Effect 经常被多个 Material 组件引用。
//这允许只创建一次效果、技术、传递和着色器,同时允许通过添加参数实例来指定材质。
//在材质上定义的参数会覆盖在效果、、技术和 RenderPass 中定义的参数(同名),但会被 RenderPassFilter 和 TechniqueFilter 中的参数覆盖。
Material {
property Buffer dataBuffer;
property real particleStep: 0.4
property real finalCollisionFactor: 0.2
parameters: [
Parameter { name: "particleStep"; value: particleStep },
Parameter { name: "finalCollisionFactor"; value: finalCollisionFactor }
]
//Effect Qt 3D 场景中效果的基类
//效果类型结合了一组技术和这些技术使用的参数来为材质产生渲染效果。
//一个 Effect 实例应该在可能的情况下在多个 Material 实例之间共享。
//在 Effect 上定义的参数会覆盖在 Technique 和 RenderPass 中定义的参数(同名)
//但会被 RenderPassFilter、TechniqueFilter 和 Material 中的参数覆盖。
//注意:效果节点不能被禁用。
//Technique 指定了一组 RenderPass 对象、FilterKey 对象、Parameter 对象和一个 GraphicsApiFilter,
//它们共同定义了给定图形 API 可以呈现的呈现技术。
//TechniqueFilter 使用过滤器键来选择 frameGraph 特定部分的特定技术。
//在 Technique 上定义的参数会覆盖 RenderPass 中定义的参数(同名),但会被
//RenderPassFilter、TechniqueFilter、Material 和 Effect 中的参数覆盖。
//在创建以图形 API 的多个版本为目标的 Effect 时,
//创建多个 Technique 节点很有用,
//每个节点都设置了 graphicsApiFilter 以匹配目标版本之一。
//在运行时,Qt3D 渲染器将根据支持的图形 API 版本和(如果指定)
//满足 frameGraph 中给定 TechniqueFilter 的 FilterKey 节点来选择最合适的 Technique。
//注意:当使用 OpenGL 作为图形 API 进行渲染时,
//Qt3D 依赖于 QSurfaceFormat::defaultFormat() 在运行时返回的
//QSurfaceFormat 来决定什么是最合适的可用 GL 版本。
//如果您需要自定义 QSurfaceFormat,请不要忘记使用QSurfaceFormat::setDefaultFormat() 来应用它。
//在视图上设置 QSurfaceFormat 可能对 Qt3D 相关渲染没有影响。
//注意:技术节点不能被禁用。
//RenderPass 指定技术使用的渲染通道
//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 节点来重载此基本渲染状态。
//ShaderProgram 类封装了一个着色器程序。
//一个着色器程序由几个不同的着色器组成,
//例如顶点着色器和片段着色器。
//Uniform 关联的 Qt3D 参数名称 GLSL声明
//ModelMatrix模型矩阵 modelMatrix uniform mat4 modelMatrix;
//ViewMatrix视图矩阵 viewMatrix uniform mat4 viewMatrix
//ProjectionMatrix投影矩阵 projectionMatrix uniform mat4 projectionMatrix;
//ModelViewMatrix模型视图矩阵 modelView uniform mat4 modelView
//ViewProjectionMatrix视图投影矩阵 viewProjectionMatrix uniform mat4 viewProjectionMatrix;
//ModelViewProjectionMatrix模型视图投影矩阵 modelViewProjection mvp uniform mat4 modelViewProjection;
uniform mat4 mvp;
//InverseModelMatrix逆模型矩阵 inverseModelMatrix uniform mat4 inverseModelMatrix;
//InverseViewMatrix逆视矩阵 inverseViewMatrix uniform mat4 inverseViewMatrix;
//InverseProjectionMatrix逆投影矩阵 uniform mat4 inverseProjectionMatrix;
//InverseModelViewMatrix逆模型视图矩阵 inverseModelView uniform mat4 inverseModelView;
//InverseViewProjectionMatrix逆视投影矩阵 inverseViewProjectionMatrix uniform mat4 inverseViewProjectionMatrix;
//InverseModelViewProjectionMatrix 逆模型视图投影矩阵 uniform mat4 inverseModelViewProjection;
//ModelNormalMatrix模型法向量矩阵 modelNormalMatrix uniform mat3 modelNormalMatrix;
//ModelViewNormalMatrix模型视图法线矩阵 modelViewNormal uniform mat3 modelViewNormal;
//ViewportMatrix视口矩阵 viewportMatrix uniform mat4 viewportMatrix;
//InverseViewportMatrix逆视口矩阵 inverseViewportMatrix uniform mat4 inverseViewportMatrix;
//AspectRatio(surface width / surface height)纵横比 aspectRatio uniform float aspectRatio;
//Exposure exposure uniform float exposure;
//Gamma Gamma uniform float gamma;
//Time(in nano seconds) time uniform float time;
//EyePosition eyePosition uniform vec3 eyePosition;
//SkinningPalette蒙皮调色板 skinningPalette[0] const int maxJoints = 100;
uniform mat4 skinningPalette[maxJoints];
//RHI 支持
//在编写 GLSL 450 着色器代码以与 Qt 3D 的 RHI 后端一起使用时,默认统一将作为 2 个统一缓冲区对象提供。
//这些的绑定位置设置为 RenderView 制服的绑定 0 和命令制服的 1。
//对于用户定义的统一缓冲区对象,使用从 2 开始的绑定或自动让 Qt 3D 自动计算绑定。
//确保在不同着色器阶段之间保持一致。
//对于上面的示例,可以通过以下方式设置 myColor:
//Parameter{name:"myColor";value:"blue"}
//纹理仍然必须定义为独立的uniforms。
effect: Effect {
techniques: [
Technique {
renderPasses: [
RenderPass {
shaderProgram: ShaderProgram {
//computeShaderCode保存此着色器程序使用的计算着色器代码。
computeShaderCode: loadSource("qrc:/shaders/gl43/particles.comp")
}
//我们将缓冲区设置为参数数据
parameters: [
Parameter { name: "Particles"; value: dataBuffer }
]
}
]
//指定启用此技术的过滤器键列表
filterKeys: [
FilterKey { name: "type"; value: "compute" }
]
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 4
minorVersion: 3
}
},
Technique {
renderPasses: [
RenderPass {
shaderProgram: ShaderProgram {
vertexShaderCode: loadSource("qrc:/shaders/gl43/particles.vert")
fragmentShaderCode: loadSource("qrc:/shaders/gl43/particles.frag")
}
// We assume the mesh to be drawn will also receive
// Vertex buffers attributes that will be used to position and color
}
]
filterKeys: [
FilterKey { name: "type"; value: "draw" }
]
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 4
minorVersion: 3
}
},
Technique {
renderPasses: [
RenderPass {
shaderProgram: ShaderProgram {
computeShaderCode: loadSource("qrc:/shaders/gl45/particles.comp")
}
// We set the buffer as the parameter data
parameters: [
Parameter { name: "Particles"; value: dataBuffer }
]
}
]
filterKeys: [
FilterKey { name: "type"; value: "compute" }
]
graphicsApiFilter {
api: GraphicsApiFilter.RHI
profile: GraphicsApiFilter.NoProfile
majorVersion: 1
minorVersion: 0
}
},
Technique {
renderPasses: [
RenderPass {
shaderProgram: ShaderProgram {
vertexShaderCode: loadSource("qrc:/shaders/gl45/particles.vert")
fragmentShaderCode: loadSource("qrc:/shaders/gl45/particles.frag")
}
// We assume the mesh to be drawn will also receive
// Vertex buffers attributes that will be used to position and color
}
]
filterKeys: [
FilterKey { name: "type"; value: "draw" }
]
graphicsApiFilter {
api: GraphicsApiFilter.RHI
profile: GraphicsApiFilter.NoProfile
majorVersion: 1
minorVersion: 0
}
}
] // techniques
}
}
ParticlesScene.qml
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Extras 2.0
Entity {
property alias particleStep: computeMaterial.particleStep
property alias finalCollisionFactor: computeMaterial.finalCollisionFactor
readonly property int _SPHERE: 0
readonly property int _CUBE: 1
readonly property int _CYLINDER: 2
readonly property int _TORUS: 3
property int particlesShape: _SPHERE
signal reset()
components: [
//RenderSettings 类型保存与渲染过程相关的设置并托管活动的 frameGraph
//RenderSettings 组件必须设置为场景根实体的组件。 它指定渲染策略和拾取设置,以及托管活动的 frameGraph。
RenderSettings {
ComputeframeGraph {
camera: sceneCamera
}
// 将 RenderingPolicy 显式设置为 AlwaysRender,
// 因为场景中的更改不会反射在实际的 Qt 场景图更改中(由于 GPU 计算调用)
//渲染策略
//Qt3DRender::QRenderSettings::onDemand 0
//frameGraph 仅在发生变化时呈现。
//Qt3DRender::QRenderSettings::Always
//即使没有任何变化,frameGraph 也会连续呈现。
renderPolicy: RenderSettings.Always
}
]
//FirstPersonCameraController 允许从第一人称视角控制场景相机
FirstPersonCameraController
{
//保存当前控制的摄像机。
camera: sceneCamera
}
Camera {
id: sceneCamera
//保存相机投影的类型。 默认值为 CameraLens.PerspectiveProjection。
projectionType: CameraLens.PerspectiveProjection
viewCenter: Qt.vector3d(0, 0, 0)
position: Qt.vector3d(0, 0, -800)
nearPlane: 0.1
farPlane: 1000
//以度为单位保持相机的当前垂直视野。
//与 aspectRatio 一起,此属性确定了相机可见的场景的多少。
//在这方面,您可能会认为它类似于选择广角(宽水平视野)或长焦(窄水平视野)镜头,具体取决于您想要捕捉多少场景。
fieldOfView: 25
//保持相机的当前纵横比。
aspectRatio: 1.33
}
property int particlesCount: 50 * 1024
readonly property int floatSize: 4
function buildParticlesBuffer() {
var byteSizeOfParticleData = 12;
var bufferData = new Float32Array(particlesCount * byteSizeOfParticleData);
var factor = 500.0;
for (var i = 0; i < particlesCount; ++i) {
var positionIdx = i * byteSizeOfParticleData;
var velocityIdx = i * byteSizeOfParticleData + 4;
var colorIdx = i * byteSizeOfParticleData + 8;
for (var j = 0; j < 3; ++j) {
bufferData[positionIdx + j] = (Math.random() - 0.5) * factor;
bufferData[velocityIdx + j] = Math.random() * 2.0;
bufferData[colorIdx + j] = 0.75 + Math.sin(((i / 1024.0) + j * 0.333) * 6.0) * 0.25;
}
bufferData[positionIdx + 3] = 1.0;
bufferData[velocityIdx + 3] = 0.0;
bufferData[colorIdx + 3] = 1.0;
}
return bufferData
}
//为原始数据提供数据存储,以便稍后用作顶点或uniform
Buffer {
id: particleBuffer
// struct ParticleData
// {
// vec3 position; // Aligned to 4 floats
// vec3 velocity; // Aligned to 4 floats
// vec3 color; // Aligned to 4 floats
// };
data: buildParticlesBuffer()
}
onReset : {
particleBuffer.data = buildParticlesBuffer()
}
//定义一个属性以及如何从 Buffet 中读取数据
Attribute {
id: particlePositionDataAttribute
name: "particlePosition"
attributeType: Attribute.VertexAttribute
vertexbaseType: Attribute.Float
vertexSize: 3
divisor: 1
byteStride: 12 * floatSize
buffer: particleBuffer
}
Attribute {
id: particleColorDataAttribute
name: "particleColor"
attributeType: Attribute.VertexAttribute
vertexbaseType: Attribute.Float
vertexSize: 3
divisor: 1
byteOffset: 8 * floatSize
byteStride: 12 * floatSize
buffer: particleBuffer
}
ComputeMaterial {
id: computeMaterial
dataBuffer: particleBuffer
}
Entity {
id: particleComputeEntity
readonly property ComputeCommand particlesComputeJob: ComputeCommand {}
components: [
particlesComputeJob,
computeMaterial
]
}
//SphereGeometry 类型最常在 SphereMesh 类型内部使用,但也可用于自定义 GeometryRenderer 类型。
SphereGeometry {
id: sphereGeometry
rings: 10
slices: 10
radius: 1
// Additional Attributes
attributes: [
particlePositionDataAttribute,
particleColorDataAttribute
]
}
CuboidGeometry {
id: cubeGeometry
yzMeshResolution: Qt.size(2, 2)
xzMeshResolution: Qt.size(2, 2)
xyMeshResolution: Qt.size(2, 2)
// Additional Attributes
attributes: [
particlePositionDataAttribute,
particleColorDataAttribute
]
}
CylinderGeometry {
id: cylinderGeometry
rings: 10
slices: 10
radius: 1
length: 1.5
// Additional Attributes
attributes: [
particlePositionDataAttribute,
particleColorDataAttribute
]
}
TorusGeometry {
id: torusGeometry
rings: 10
slices: 10
radius: 1
minorRadius: 0.5
// Additional Attributes
attributes: [
particlePositionDataAttribute,
particleColorDataAttribute
]
}
Entity {
id: particleRenderEntity
readonly property GeometryRenderer particlesRenderer: GeometryRenderer {
instanceCount: particlesCount
indexOffset: 0
firstInstance: 0
primitiveType: GeometryRenderer.Triangles
geometry: {
switch (particlesShape) {
case _SPHERE:
return sphereGeometry;
case _CUBE:
return cubeGeometry;
case _CYLINDER:
return cylinderGeometry;
case _TORUS:
return torusGeometry;
}
}
}
components: [
particlesRenderer,
computeMaterial
]
}
}
particles.comp
#version 430 core
uniform float particleStep;
uniform float finalCollisionFactor;
layout (local_size_x = 1024) in;
struct ParticleData
{
vec4 position;
vec4 direction;
vec4 color;
};
// Particles from previouse frame
layout (std430, binding = 0) coherent buffer Particles
{
ParticleData particles[];
} data;
void main(void)
{
uint globalId = gl_GlobalInvocationID.x;
// 从前一帧中检索当前粒子
ParticleData currentParticle = data.particles[globalId];
// 新位置 = 旧位置 + 跨步持续时间
currentParticle.position = currentParticle.position + currentParticle.direction * particleStep;
// 使加速度或多或少指向场景的中心
vec4 acceleration = normalize(vec4(0.0) - currentParticle.position) * finalCollisionFactor;
// 新速度 = 旧速度 + 跨步持续时间的加速度
currentParticle.direction = currentParticle.direction + acceleration * particleStep;
// 保存更新的粒子
data.particles[globalId] = currentParticle;
}
particles.vert
#version 430 core
in vec3 vertexPosition;
in vec3 vertexNormal;
in vec3 particlePosition;
in vec3 particleColor;
out VertexBlock
{
flat vec3 color;
vec3 pos;
vec3 normal;
} v_out;
uniform mat4 mvp;
uniform mat3 modelViewNormal;
uniform mat4 modelView;
void main(void)
{
vec4 pos = vec4(vertexPosition.xyz, 1.0) + vec4(particlePosition, 0.0);
gl_Position = mvp * pos;
v_out.pos = vec4(modelView * pos).xyz;
v_out.normal = normalize(modelViewNormal * vertexNormal);
v_out.color = mix(particleColor * 0.2, particleColor, smoothstep(0.5, 0.8, abs(v_out.normal).z));
}
particles.frag
#version 430 core
out vec4 color;
in VertexBlock
{
flat vec3 color;
vec3 pos;
vec3 normal;
} frag_in;
const vec4 lightPosition = vec4(0.0, 0.0, 0.0, 0.0);
const vec3 lightIntensity = vec3(1.0, 1.0, 1.0);
const vec3 ka = vec3(0.1, 0.1, 0.1);
const vec3 ks = vec3(0.8, 0.8, 0.8);
const float shininess = 50.0;
vec3 ads()
{
vec3 n = normalize( frag_in.normal);
vec3 s = normalize( vec3(lightPosition) - frag_in.pos );
vec3 v = normalize( -frag_in.pos );
vec3 h = normalize( v + s );
return lightIntensity * (ka +
frag_in.color * max( dot(s, frag_in.normal ), 0.0 ) +
ks * pow( max( dot( h, n ), 0.0 ), shininess ) );
}
void main(void)
{
color = vec4(ads(), 1.0);
}



