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)";
33
34void main()
35{
36 qt_customMain();
37}
38
39)";
42// Helper: outer square border of a face
43float line(vec2 uv, float width)
44{
45 vec2 d = abs(uv - 0.5);
46 return step(max(d.x, d.y), 0.5) - step(max(d.x, d.y), 0.5 - width);
47}
48
49// Simple plus/minus symbol
50float drawPlus(vec2 uv)
51{
52 float h = step(abs(uv.y - 0.5), 0.05) * step(abs(uv.x - 0.5), 0.2);
53 float v = step(abs(uv.x - 0.5), 0.05) * step(abs(uv.y - 0.5), 0.2);
54 return max(h, v);
55}
56
57float drawMinus(vec2 uv)
58{
59 return step(abs(uv.y - 0.5), 0.05) * step(abs(uv.x - 0.5), 0.2);
60}
61
62void MAIN()
63{
64 vec3 d = normalize(qt_eyeDir);
65 vec3 ad = abs(d);
66
67 vec2 uv;
68 vec3 faceColor;
69 float label = 0.0;
70
71 // Determine dominant axis (cubemap face)
72 if (ad.x > ad.y && ad.x > ad.z)
73 {
74 uv = d.zy / ad.x * 0.5 + 0.5;
75 if (d.x > 0.0)
76 {
77 faceColor = vec3(1.0, 0.0, 0.0); // +X red
78 label = drawPlus(uv);
79 }
80 else
81 {
82 faceColor = vec3(0.5, 0.0, 0.0); // -X dark red
83 label = drawMinus(uv);
84 }
85 }
86 else if (ad.y > ad.x && ad.y > ad.z)
87 {
88 uv = d.xz / ad.y * 0.5 + 0.5;
89 if (d.y > 0.0)
90 {
91 faceColor = vec3(0.0, 1.0, 0.0); // +Y green
92 label = drawPlus(uv);
93 }
94 else
95 {
96 faceColor = vec3(0.0, 0.5, 0.0); // -Y dark green
97 label = drawMinus(uv);
98 }
99 }
100 else
101 {
102 uv = d.xy / ad.z * 0.5 + 0.5;
103 if (d.z > 0.0)
104 {
105 faceColor = vec3(0.0, 0.0, 1.0); // +Z blue
106 label = drawPlus(uv);
107 }
108 else
109 {
110 faceColor = vec3(0.0, 0.0, 0.5); // -Z dark blue
111 label = drawMinus(uv);
112 }
113 }
114
115 // Outer border
116 float border = line(uv, 0.02);
117
118 // Combine
119 vec3 color = faceColor;
120 color = mix(color, vec3(1.0), border); // white border
121 color = mix(color, vec3(1.0), label); // white + or - label
122
123 FRAGCOLOR = vec4(color, 1.0);
124}
125)";
127QSSGRenderSkyMaterial::QSSGRenderSkyMaterial() : QSSGRenderGraphObject(QSSGRenderGraphObject::Type::SkyMaterial) { }
129QSSGRenderSkyMaterial::~QSSGRenderSkyMaterial() =
default;
131QSSGRhiShaderPipelinePtr QSSGRenderSkyMaterial::ensurePipeline(
const QSSGRenderContextInterface &sgContext)
133 if (iblPassPipeline && !isFragmentShaderDirty)
134 return iblPassPipeline;
136 auto &bufferManager = *sgContext.bufferManager().get();
141 for (
const auto &u : std::as_const(propertyUniforms)) {
142 if (u.shaderDataType == QSSGRenderShaderValue::Texture) {
143 QSSGRenderImage *image = u.value.value<QSSGRenderImage *>();
144 const QSSGRenderImageTexture texture = image ? bufferManager.loadRenderImage(image) : QSSGRenderImageTexture { };
145 if (!image || !texture.m_texture) {
151 QByteArray vertexShader = vertexShaderStr;
152 QByteArray fragmentShader = (!fragmentShaderSource.isEmpty() ? fragmentShaderSource : QByteArray(debugFragStr)) + mainFragmentSnippet;
154 QSSGShaderCustomMaterialAdapter::StringPairList baseUniforms;
155 for (
const auto &u : std::as_const(propertyUniforms))
156 baseUniforms.append({ u.typeName, u.name });
158 QSSGShaderCustomMaterialAdapter::StringPairList inputOutputs;
159 inputOutputs.append({
"vec3",
"qt_eyeDir"_ba });
162 QSSGShaderCustomMaterialAdapter::StringPairList vertexUniforms;
163 vertexUniforms.append({
"mat4"_ba,
"qt_projectionMatrix"_ba });
164 vertexUniforms.append({
"mat4"_ba,
"qt_viewMatrix"_ba });
166 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
168 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
169 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, vertexShader, QSSGShaderCache::ShaderType::Vertex,
false);
170 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
173 QSSGShaderCache::ShaderType::Vertex,
180 vertexShader = result.first;
181 vertexShader.append(buf);
185 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
187 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
188 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, fragmentShader, QSSGShaderCache::ShaderType::Fragment,
false);
189 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
192 QSSGShaderCache::ShaderType::Fragment,
199 fragmentShader = result.first;
200 fragmentShader.append(buf);
203 auto generator = sgContext.shaderProgramGenerator().get();
204 auto shaderLib = sgContext.shaderLibraryManager().get();
205 auto shaderCache = sgContext.shaderCache().get();
207 generator->beginProgram();
208 auto vertex = generator->getStage(QSSGShaderGeneratorStage::Vertex);
209 vertex->addIncoming(
"attr_pos"_ba,
"vec3"_ba);
210 vertex->append(vertexShader);
212 auto fragment = generator->getStage(QSSGShaderGeneratorStage::Fragment);
213 fragment->addInclude(
"tonemapping.glsllib"_ba);
214 fragment->append(fragmentShader);
216 QSSGShaderFeatures features;
217 features.set(QSSGShaderFeatures::Feature::AcesTonemapping,
true);
218 const QByteArray key = shaderPathKey +
':'
219 + QCryptographicHash::hash(QByteArray(vertexShader + fragmentShader), QCryptographicHash::Algorithm::Sha1).toHex();
220 iblPassPipeline = generator->compileGeneratedRhiShader(key, features, *shaderLib, *shaderCache, QSSGRhiShaderPipeline::UsedWithoutIa, { }, 1,
false);
222 isFragmentShaderDirty =
false;
224 return iblPassPipeline;
227quint32 QSSGRenderSkyMaterial::updateUniforms(
const QSSGRenderContextInterface &sgContext,
228 const QMatrix4x4 &mvp,
229 const QVarLengthArray<QMatrix4x4, 6> views)
231 constexpr int cMatrixSize = 64;
233 if (!iblPassPipeline)
236 QSSGRhiContext *rhiCtx = sgContext.rhiContext().get();
237 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
238 QSSGRhiDrawCallData *dcd = &rhiCtxD->drawCallData({ (
void *)
this,
nullptr,
nullptr, 0 });
240 const int uniformStride = rhiCtx->rhi()->ubufAligned(iblPassPipeline->ub0Size());
241 const int totalBufferSize = uniformStride * 6;
244 dcd->ubuf = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalBufferSize);
248 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
249 auto bufferManager = sgContext.bufferManager().get();
251 iblPassPipeline->resetExtraTextures();
253 for (
const auto &u : std::as_const(propertyUniforms))
254 iblPassPipeline->setShaderResources(ubufData, *bufferManager, u.name, u.value, u.shaderDataType);
257 iblPassPipeline->setShaderResources(ubufData, *bufferManager,
"qt_projectionMatrix"_ba, mvp, QSSGRenderShaderValue::Type::Matrix4x4);
258 iblPassPipeline->setShaderResources(ubufData, *bufferManager,
"qt_viewMatrix"_ba, views[0], QSSGRenderShaderValue::Type::Matrix4x4);
263 const int viewMatrixOffset = iblPassPipeline->offsetOfUniform(
"qt_viewMatrix"_ba);
265 Q_ASSERT(ubufData !=
nullptr);
266 Q_ASSERT(totalBufferSize >= 6 * uniformStride);
267 Q_ASSERT(uniformStride >= cMatrixSize);
268 Q_ASSERT(viewMatrixOffset >= 0);
269 Q_ASSERT(viewMatrixOffset + cMatrixSize <= uniformStride);
271 auto buffer = QSpan<
char>(ubufData, totalBufferSize);
272 auto firstFace = buffer.first(uniformStride);
274 for (
int face = 1; face < 6; ++face) {
275 auto dst = buffer.sliced(face * uniformStride, uniformStride);
278 std::copy(firstFace.begin(), firstFace.end(), dst.begin());
281 std::copy_n(
reinterpret_cast<
const char *>(views[face].constData()), cMatrixSize, dst.data() + viewMatrixOffset);
283 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
285 rhiCtxD->releaseCachedSrb(bindings);
288 bindings = QSSGRhiShaderResourceBindingList();
290 bindings.addUniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, dcd->ubuf, 0, uniformStride,
true);
293 int maxSamplerBinding = -1;
295 QVector<QShaderDescription::InOutVariable>
296 samplerVars = iblPassPipeline->fragmentStage()->shader().description().combinedImageSamplers();
297 for (
const QShaderDescription::InOutVariable &var :
298 iblPassPipeline->vertexStage()->shader().description().combinedImageSamplers()) {
299 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(), [&var](
const QShaderDescription::InOutVariable &v) {
300 return var.binding == v.binding;
302 if (it == samplerVars.cend())
303 samplerVars.append(var);
305 for (
const QShaderDescription::InOutVariable &var : std::as_const(samplerVars))
306 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
308 if (maxSamplerBinding >= 0) {
310 int customTexCount = iblPassPipeline->extraTextureCount();
311 for (
int i = 0; i < customTexCount; ++i) {
312 const QSSGRhiTexture &t(iblPassPipeline->extraTextureAt(i));
313 const int samplerBinding = iblPassPipeline->bindingForTexture(t.name);
314 if (samplerBinding >= 0) {
315 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
316 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, t.texture, sampler);
322 return uniformStride;
static const char * mainFragmentSnippet
static QT_BEGIN_NAMESPACE const char * vertexShaderStr
static const char * debugFragStr