6#include <QtQuick3DRuntimeRender/private/qssgrenderskymaterial_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrendererimplshaders_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrhieffectsystem_p.h>
9#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
11#include <QtCore/qhash.h>
12#include <QtCore/qspan.h>
13#include <QtGui/qvector4d.h>
14#include <rhi/qshaderbaker.h>
18using namespace Qt::StringLiterals;
23void main()
24{
25 vec4 qt_vertPosition = vec4(attr_pos, 1.0);
26 qt_eyeDir = qt_vertPosition.xyz;
27 gl_Position = qt_projectionMatrix * qt_viewMatrix * vec4(qt_eyeDir, 1.0);
28}
29
30)";
38void main()
39{
40 gl_Position = vec4(attr_pos, 1.0);
41#if QSHADER_VIEW_COUNT >= 2
42 vec3 qt_unprojected = (qt_inverseProjection[gl_ViewIndex] * gl_Position).xyz;
43 qt_eyeDir = (qt_viewMatrix[gl_ViewIndex] * vec4(qt_unprojected, 0.0)).xyz;
44#else
45 vec3 qt_unprojected = (qt_inverseProjection * gl_Position).xyz;
46 qt_eyeDir = (qt_viewMatrix * vec4(qt_unprojected, 0.0)).xyz;
47#endif
48 gl_Position.y *= qt_adjustY;
49}
50
51)";
54
55void main()
56{
57 qt_customMain();
58}
59
60)";
67
68void main()
69{
70 qt_customMain();
71 FRAGCOLOR = vec4(qt_tonemap(qt_exposure(FRAGCOLOR.rgb, qt_skyExposure)), 1.0);
72}
73
74)";
77// Helper: outer square border of a face
78float line(vec2 uv, float width)
79{
80 vec2 d = abs(uv - 0.5);
81 return step(max(d.x, d.y), 0.5) - step(max(d.x, d.y), 0.5 - width);
82}
83
84// Simple plus/minus symbol
85float drawPlus(vec2 uv)
86{
87 float h = step(abs(uv.y - 0.5), 0.05) * step(abs(uv.x - 0.5), 0.2);
88 float v = step(abs(uv.x - 0.5), 0.05) * step(abs(uv.y - 0.5), 0.2);
89 return max(h, v);
90}
91
92float drawMinus(vec2 uv)
93{
94 return step(abs(uv.y - 0.5), 0.05) * step(abs(uv.x - 0.5), 0.2);
95}
96
97void MAIN()
98{
99 vec3 d = normalize(qt_eyeDir);
100 vec3 ad = abs(d);
101
102 vec2 uv;
103 vec3 faceColor;
104 float label = 0.0;
105
106 // Determine dominant axis (cubemap face)
107 if (ad.x > ad.y && ad.x > ad.z)
108 {
109 uv = d.zy / ad.x * 0.5 + 0.5;
110 if (d.x > 0.0)
111 {
112 faceColor = vec3(1.0, 0.0, 0.0); // +X red
113 label = drawPlus(uv);
114 }
115 else
116 {
117 faceColor = vec3(0.5, 0.0, 0.0); // -X dark red
118 label = drawMinus(uv);
119 }
120 }
121 else if (ad.y > ad.x && ad.y > ad.z)
122 {
123 uv = d.xz / ad.y * 0.5 + 0.5;
124 if (d.y > 0.0)
125 {
126 faceColor = vec3(0.0, 1.0, 0.0); // +Y green
127 label = drawPlus(uv);
128 }
129 else
130 {
131 faceColor = vec3(0.0, 0.5, 0.0); // -Y dark green
132 label = drawMinus(uv);
133 }
134 }
135 else
136 {
137 uv = d.xy / ad.z * 0.5 + 0.5;
138 if (d.z > 0.0)
139 {
140 faceColor = vec3(0.0, 0.0, 1.0); // +Z blue
141 label = drawPlus(uv);
142 }
143 else
144 {
145 faceColor = vec3(0.0, 0.0, 0.5); // -Z dark blue
146 label = drawMinus(uv);
147 }
148 }
149
150 // Outer border
151 float border = line(uv, 0.02);
152
153 // Combine
154 vec3 color = faceColor;
155 color = mix(color, vec3(1.0), border); // white border
156 color = mix(color, vec3(1.0), label); // white + or - label
157
158 FRAGCOLOR = vec4(color, 1.0);
159}
160)";
162QSSGRenderSkyMaterial::QSSGRenderSkyMaterial() : QSSGRenderGraphObject(QSSGRenderGraphObject::Type::SkyMaterial) { }
164QSSGRenderSkyMaterial::~QSSGRenderSkyMaterial() =
default;
171 for (
const auto &u : std::as_const(propertyUniforms)) {
172 if (u.shaderDataType == QSSGRenderShaderValue::Texture) {
173 QSSGRenderImage *image = u.value.value<QSSGRenderImage *>();
174 const QSSGRenderImageTexture texture = image ? bufferManager.loadRenderImage(image) : QSSGRenderImageTexture { };
175 if (!image || !texture.m_texture)
185 QSSGRhiShaderResourceBindingList &bindings,
186 QSSGRhiContext *rhiCtx)
188 int maxSamplerBinding = -1;
189 QVector<QShaderDescription::InOutVariable> samplerVars =
190 pipeline->fragmentStage()->shader().description().combinedImageSamplers();
191 for (
const QShaderDescription::InOutVariable &var :
192 pipeline->vertexStage()->shader().description().combinedImageSamplers()) {
193 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(), [&var](
const QShaderDescription::InOutVariable &v) {
194 return var.binding == v.binding;
196 if (it == samplerVars.cend())
197 samplerVars.append(var);
199 for (
const QShaderDescription::InOutVariable &var : std::as_const(samplerVars))
200 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
202 if (maxSamplerBinding < 0)
205 const int customTexCount = pipeline->extraTextureCount();
206 for (
int i = 0; i < customTexCount; ++i) {
207 const QSSGRhiTexture &t(pipeline->extraTextureAt(i));
208 const int samplerBinding = pipeline->bindingForTexture(t.name);
209 if (samplerBinding >= 0) {
210 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
211 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, t.texture, sampler);
221QSSGRhiShaderPipelinePtr QSSGRenderSkyMaterial::buildPipeline(
const QSSGRenderContextInterface &sgContext,
222 QByteArray vertexShader,
223 const QSSGShaderCustomMaterialAdapter::StringPairList &vertexViewDependentUniforms,
224 const QSSGShaderCustomMaterialAdapter::StringPairList &vertexUniforms,
225 const QByteArray &fragmentMainSnippet,
226 const QSSGShaderCustomMaterialAdapter::StringPairList &fragmentUniforms,
227 const QSSGShaderFeatures &features,
229 const QByteArray &cacheKeyTag)
231 const bool multiViewCompatible = viewCount >= 2;
233 QByteArray fragmentShader = (!fragmentShaderSource.isEmpty() ? fragmentShaderSource : QByteArray(debugFragStr)) + fragmentMainSnippet;
235 QSSGShaderCustomMaterialAdapter::StringPairList propertyBaseUniforms;
236 for (
const auto &u : std::as_const(propertyUniforms))
237 propertyBaseUniforms.append({ u.typeName, u.name });
239 QSSGShaderCustomMaterialAdapter::StringPairList inputOutputs;
240 inputOutputs.append({
"vec3",
"qt_eyeDir"_ba });
243 QSSGShaderCustomMaterialAdapter::StringPairList vertexBaseUniforms = propertyBaseUniforms;
244 vertexBaseUniforms.append(vertexUniforms.constData(), vertexUniforms.size());
246 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
248 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
249 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, vertexShader, QSSGShaderCache::ShaderType::Vertex, multiViewCompatible);
250 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
253 QSSGShaderCache::ShaderType::Vertex,
259 vertexViewDependentUniforms);
260 vertexShader = result.first;
261 vertexShader.append(buf);
266 QSSGShaderCustomMaterialAdapter::StringPairList fragmentBaseUniforms = propertyBaseUniforms;
267 fragmentBaseUniforms.append(fragmentUniforms.constData(), fragmentUniforms.size());
269 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
271 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
272 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, fragmentShader, QSSGShaderCache::ShaderType::Fragment, multiViewCompatible);
273 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
276 QSSGShaderCache::ShaderType::Fragment,
278 fragmentBaseUniforms,
283 fragmentShader = result.first;
284 fragmentShader.append(buf);
287 auto generator = sgContext.shaderProgramGenerator().get();
288 auto shaderLib = sgContext.shaderLibraryManager().get();
289 auto shaderCache = sgContext.shaderCache().get();
291 generator->beginProgram();
292 auto vertex = generator->getStage(QSSGShaderGeneratorStage::Vertex);
293 vertex->addIncoming(
"attr_pos"_ba,
"vec3"_ba);
294 vertex->append(vertexShader);
296 auto fragment = generator->getStage(QSSGShaderGeneratorStage::Fragment);
297 fragment->addInclude(
"tonemapping.glsllib"_ba);
298 fragment->append(fragmentShader);
300 const QByteArray key = shaderPathKey + cacheKeyTag +
':'
301 + QCryptographicHash::hash(QByteArray(vertexShader + fragmentShader), QCryptographicHash::Algorithm::Sha1).toHex();
302 return generator->compileGeneratedRhiShader(key, features, *shaderLib, *shaderCache, QSSGRhiShaderPipeline::UsedWithoutIa, { }, viewCount,
false);
305QSSGRhiShaderPipelinePtr QSSGRenderSkyMaterial::ensurePipeline(
const QSSGRenderContextInterface &sgContext)
307 if (iblPassPipeline && !isFragmentShaderDirty)
308 return iblPassPipeline;
310 if (!skyShaderTexturesReady(propertyUniforms, *sgContext.bufferManager().get()))
313 QSSGShaderCustomMaterialAdapter::StringPairList vertexViewDependentUniforms;
314 vertexViewDependentUniforms.append({
"mat4"_ba,
"qt_projectionMatrix"_ba });
315 vertexViewDependentUniforms.append({
"mat4"_ba,
"qt_viewMatrix"_ba });
320 QSSGShaderFeatures features;
321 features.set(QSSGShaderFeatures::Feature::AcesTonemapping,
true);
323 iblPassPipeline = buildPipeline(sgContext, vertexShaderStr, vertexViewDependentUniforms, { }, mainFragmentSnippet, { }, features, 1, QByteArrayLiteral(
":cube"));
325 isFragmentShaderDirty =
false;
327 return iblPassPipeline;
330QSSGRhiShaderPipelinePtr QSSGRenderSkyMaterial::ensureBackgroundPipeline(
const QSSGRenderContextInterface &sgContext,
331 const QSSGShaderFeatures &tonemapFeatures,
335 if (backgroundPipeline && !isBackgroundShaderDirty && m_backgroundTonemapKey == tonemapKey
336 && m_backgroundViewCount == viewCount) {
337 return backgroundPipeline;
340 if (!skyShaderTexturesReady(propertyUniforms, *sgContext.bufferManager().get()))
343 QSSGShaderCustomMaterialAdapter::StringPairList vertexViewDependentUniforms;
344 vertexViewDependentUniforms.append({
"mat4"_ba,
"qt_inverseProjection"_ba });
345 vertexViewDependentUniforms.append({
"mat4"_ba,
"qt_viewMatrix"_ba });
347 QSSGShaderCustomMaterialAdapter::StringPairList vertexUniforms;
348 vertexUniforms.append({
"float"_ba,
"qt_adjustY"_ba });
350 QSSGShaderCustomMaterialAdapter::StringPairList fragmentUniforms;
351 fragmentUniforms.append({
"float"_ba,
"qt_skyExposure"_ba });
353 backgroundPipeline = buildPipeline(sgContext, vertexShaderStrScreen, vertexViewDependentUniforms, vertexUniforms,
354 mainFragmentSnippetScreen, fragmentUniforms, tonemapFeatures, viewCount,
355 QByteArrayLiteral(
":screen:") + QByteArray::number(tonemapKey)
356 +
':' + QByteArray::number(viewCount));
358 m_backgroundTonemapKey = tonemapKey;
359 m_backgroundViewCount = viewCount;
360 isBackgroundShaderDirty =
false;
362 return backgroundPipeline;
365quint32 QSSGRenderSkyMaterial::updateUniforms(
const QSSGRenderContextInterface &sgContext,
366 const QMatrix4x4 &mvp,
367 const QVarLengthArray<QMatrix4x4, 6> views)
369 constexpr int cMatrixSize = 64;
371 if (!iblPassPipeline)
374 QSSGRhiContext *rhiCtx = sgContext.rhiContext().get();
375 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
376 QSSGRhiDrawCallData *dcd = &rhiCtxD->drawCallData({ (
void *)
this,
nullptr,
nullptr, 0 });
378 const int uniformStride = rhiCtx->rhi()->ubufAligned(iblPassPipeline->ub0Size());
379 const int totalBufferSize = uniformStride * 6;
382 dcd->ubuf = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalBufferSize);
386 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
387 auto bufferManager = sgContext.bufferManager().get();
389 iblPassPipeline->resetExtraTextures();
391 for (
const auto &u : std::as_const(propertyUniforms))
392 iblPassPipeline->setShaderResources(ubufData, *bufferManager, u.name, u.value, u.shaderDataType);
395 iblPassPipeline->setShaderResources(ubufData, *bufferManager,
"qt_projectionMatrix"_ba, mvp, QSSGRenderShaderValue::Type::Matrix4x4);
396 iblPassPipeline->setShaderResources(ubufData, *bufferManager,
"qt_viewMatrix"_ba, views[0], QSSGRenderShaderValue::Type::Matrix4x4);
401 const int viewMatrixOffset = iblPassPipeline->offsetOfUniform(
"qt_viewMatrix"_ba);
403 Q_ASSERT(ubufData !=
nullptr);
404 Q_ASSERT(totalBufferSize >= 6 * uniformStride);
405 Q_ASSERT(uniformStride >= cMatrixSize);
406 Q_ASSERT(viewMatrixOffset >= 0);
407 Q_ASSERT(viewMatrixOffset + cMatrixSize <= uniformStride);
409 auto buffer = QSpan<
char>(ubufData, totalBufferSize);
410 auto firstFace = buffer.first(uniformStride);
412 for (
int face = 1; face < 6; ++face) {
413 auto dst = buffer.sliced(face * uniformStride, uniformStride);
416 std::copy(firstFace.begin(), firstFace.end(), dst.begin());
419 std::copy_n(
reinterpret_cast<
const char *>(views[face].constData()), cMatrixSize, dst.data() + viewMatrixOffset);
421 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
423 rhiCtxD->releaseCachedSrb(bindings);
424 bindings = QSSGRhiShaderResourceBindingList();
425 bindings.addUniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, dcd->ubuf, 0, uniformStride,
true);
426 appendCustomTextureBindings(iblPassPipeline.get(), bindings, rhiCtx);
428 return uniformStride;
431void QSSGRenderSkyMaterial::updateBackgroundUniforms(
const QSSGRenderContextInterface &sgContext,
432 const QVarLengthArray<QMatrix4x4, 2> &inverseProjections,
433 const QVarLengthArray<QMatrix4x4, 2> &viewRotations,
437 if (!backgroundPipeline)
440 QSSGRhiContext *rhiCtx = sgContext.rhiContext().get();
441 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
444 QSSGRhiDrawCallData *dcd = &rhiCtxD->drawCallData({ (
void *)
this,
nullptr,
nullptr, 1 });
446 const int bufferSize = rhiCtx->rhi()->ubufAligned(backgroundPipeline->ub0Size());
448 if (!dcd->ubuf ||
int(dcd->ubuf->size()) != bufferSize) {
450 dcd->ubuf = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, bufferSize);
454 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
455 auto bufferManager = sgContext.bufferManager().get();
457 backgroundPipeline->resetExtraTextures();
459 for (
const auto &u : std::as_const(propertyUniforms))
460 backgroundPipeline->setShaderResources(ubufData, *bufferManager, u.name, u.value, u.shaderDataType);
462 backgroundPipeline->setShaderResources(ubufData, *bufferManager,
"qt_adjustY"_ba, adjustY, QSSGRenderShaderValue::Type::Float);
463 backgroundPipeline->setShaderResources(ubufData, *bufferManager,
"qt_skyExposure"_ba, exposure, QSSGRenderShaderValue::Type::Float);
465 constexpr int matSize = 64;
466 const int invProjOffset = backgroundPipeline->offsetOfUniform(
"qt_inverseProjection"_ba);
467 const int viewMatOffset = backgroundPipeline->offsetOfUniform(
"qt_viewMatrix"_ba);
468 const int viewCount =
int(inverseProjections.size());
469 for (
int v = 0; v < viewCount; ++v) {
470 if (invProjOffset >= 0)
471 std::copy_n(
reinterpret_cast<
const char *>(inverseProjections[v].constData()), matSize, ubufData + invProjOffset + v * matSize);
472 if (viewMatOffset >= 0)
473 std::copy_n(
reinterpret_cast<
const char *>(viewRotations[v].constData()), matSize, ubufData + viewMatOffset + v * matSize);
476 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
478 rhiCtxD->releaseCachedSrb(backgroundBindings);
479 backgroundBindings = QSSGRhiShaderResourceBindingList();
480 backgroundBindings.addUniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, dcd->ubuf);
481 appendCustomTextureBindings(backgroundPipeline.get(), backgroundBindings, rhiCtx);
static const char * mainFragmentSnippetScreen
static void appendCustomTextureBindings(QSSGRhiShaderPipeline *pipeline, QSSGRhiShaderResourceBindingList &bindings, QSSGRhiContext *rhiCtx)
static const char * mainFragmentSnippet
static QT_BEGIN_NAMESPACE const char * vertexShaderStr
static const char * vertexShaderStrScreen
static bool skyShaderTexturesReady(const QList< QSSGBaseTypeProperty > &propertyUniforms, QSSGBufferManager &bufferManager)
static const char * debugFragStr