7
8
9
10
13#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
16#include <QtGui/qquaternion.h>
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
57
58
61
62
65
66
69
70
73
74
75
78
79
80
83
84
87
88
91
92
95
96
99
100
103
104
107
108
111
112
115
116
119
120
121
122
123
124
125
127ProceduralSkyTextureData::ProceduralSkyTextureData()
129 scheduleTextureUpdate();
132ProceduralSkyTextureData::~ProceduralSkyTextureData()
136QColor ProceduralSkyTextureData::skyTopColor()
const
138 return m_skyTopColor;
141QColor ProceduralSkyTextureData::skyHorizonColor()
const
143 return m_skyHorizonColor;
146float ProceduralSkyTextureData::skyCurve()
const
151float ProceduralSkyTextureData::skyEnergy()
const
156QColor ProceduralSkyTextureData::groundBottomColor()
const
158 return m_groundBottomColor;
161QColor ProceduralSkyTextureData::groundHorizonColor()
const
163 return m_groundHorizonColor;
166float ProceduralSkyTextureData::groundCurve()
const
168 return m_groundCurve;
171float ProceduralSkyTextureData::groundEnergy()
const
173 return m_groundEnergy;
176QColor ProceduralSkyTextureData::sunColor()
const
181float ProceduralSkyTextureData::sunLatitude()
const
183 return m_sunLatitude;
186float ProceduralSkyTextureData::sunLongitude()
const
188 return m_sunLongitude;
191float ProceduralSkyTextureData::sunAngleMin()
const
193 return m_sunAngleMin;
196float ProceduralSkyTextureData::sunAngleMax()
const
198 return m_sunAngleMax;
201float ProceduralSkyTextureData::sunCurve()
const
206float ProceduralSkyTextureData::sunEnergy()
const
211ProceduralSkyTextureData::SkyTextureQuality ProceduralSkyTextureData::textureQuality()
const
213 return m_textureQuality;
216void ProceduralSkyTextureData::setSkyTopColor(QColor skyTopColor)
218 if (m_skyTopColor == skyTopColor)
221 m_skyTopColor = skyTopColor;
222 emit skyTopColorChanged(m_skyTopColor);
223 scheduleTextureUpdate();
226void ProceduralSkyTextureData::setSkyHorizonColor(QColor skyHorizonColor)
228 if (m_skyHorizonColor == skyHorizonColor)
231 m_skyHorizonColor = skyHorizonColor;
232 emit skyHorizonColorChanged(m_skyHorizonColor);
233 scheduleTextureUpdate();
236void ProceduralSkyTextureData::setSkyCurve(
float skyCurve)
238 if (qFuzzyCompare(m_skyCurve, skyCurve))
241 m_skyCurve = skyCurve;
242 emit skyCurveChanged(m_skyCurve);
243 scheduleTextureUpdate();
246void ProceduralSkyTextureData::setSkyEnergy(
float skyEnergy)
248 if (qFuzzyCompare(m_skyEnergy, skyEnergy))
251 m_skyEnergy = skyEnergy;
252 emit skyEnergyChanged(m_skyEnergy);
253 scheduleTextureUpdate();
256void ProceduralSkyTextureData::setGroundBottomColor(QColor groundBottomColor)
258 if (m_groundBottomColor == groundBottomColor)
261 m_groundBottomColor = groundBottomColor;
262 emit groundBottomColorChanged(m_groundBottomColor);
263 scheduleTextureUpdate();
266void ProceduralSkyTextureData::setGroundHorizonColor(QColor groundHorizonColor)
268 if (m_groundHorizonColor == groundHorizonColor)
271 m_groundHorizonColor = groundHorizonColor;
272 emit groundHorizonColorChanged(m_groundHorizonColor);
273 scheduleTextureUpdate();
276void ProceduralSkyTextureData::setGroundCurve(
float groundCurve)
278 if (qFuzzyCompare(m_groundCurve, groundCurve))
281 m_groundCurve = groundCurve;
282 emit groundCurveChanged(m_groundCurve);
283 scheduleTextureUpdate();
286void ProceduralSkyTextureData::setGroundEnergy(
float groundEnergy)
288 if (qFuzzyCompare(m_groundEnergy, groundEnergy))
291 m_groundEnergy = groundEnergy;
292 emit groundEnergyChanged(m_groundEnergy);
293 scheduleTextureUpdate();
296void ProceduralSkyTextureData::setSunColor(QColor sunColor)
298 if (m_sunColor == sunColor)
301 m_sunColor = sunColor;
302 emit sunColorChanged(m_sunColor);
303 scheduleTextureUpdate();
306void ProceduralSkyTextureData::setSunLatitude(
float sunLatitude)
308 if (qFuzzyCompare(m_sunLatitude, sunLatitude))
311 m_sunLatitude = sunLatitude;
312 emit sunLatitudeChanged(m_sunLatitude);
313 scheduleTextureUpdate();
316void ProceduralSkyTextureData::setSunLongitude(
float sunLongitude)
318 if (qFuzzyCompare(m_sunLongitude, sunLongitude))
321 m_sunLongitude = sunLongitude;
322 emit sunLongitudeChanged(m_sunLongitude);
323 scheduleTextureUpdate();
326void ProceduralSkyTextureData::setSunAngleMin(
float sunAngleMin)
328 if (qFuzzyCompare(m_sunAngleMin, sunAngleMin))
331 m_sunAngleMin = sunAngleMin;
332 emit sunAngleMinChanged(m_sunAngleMin);
333 scheduleTextureUpdate();
336void ProceduralSkyTextureData::setSunAngleMax(
float sunAngleMax)
338 if (qFuzzyCompare(m_sunAngleMax, sunAngleMax))
341 m_sunAngleMax = sunAngleMax;
342 emit sunAngleMaxChanged(m_sunAngleMax);
343 scheduleTextureUpdate();
346void ProceduralSkyTextureData::setSunCurve(
float sunCurve)
348 if (qFuzzyCompare(m_sunCurve, sunCurve))
351 m_sunCurve = sunCurve;
352 emit sunCurveChanged(m_sunCurve);
353 scheduleTextureUpdate();
356void ProceduralSkyTextureData::setSunEnergy(
float sunEnergy)
358 if (qFuzzyCompare(m_sunEnergy, sunEnergy))
361 m_sunEnergy = sunEnergy;
362 emit sunEnergyChanged(m_sunEnergy);
363 scheduleTextureUpdate();
366void ProceduralSkyTextureData::setTextureQuality(ProceduralSkyTextureData::SkyTextureQuality textureQuality)
368 if (m_textureQuality == textureQuality)
371 m_textureQuality = textureQuality;
372 emit textureQualityChanged(m_textureQuality);
373 scheduleTextureUpdate();
376void ProceduralSkyTextureData::generateRGBA16FTexture()
379 switch (m_textureQuality) {
380 case SkyTextureQuality::SkyTextureQualityLow:
383 case SkyTextureQuality::SkyTextureQualityMedium:
386 case SkyTextureQuality::SkyTextureQualityHigh:
389 case SkyTextureQuality::SkyTextureQualityVeryHigh:
394 const int width = size;
395 const int height = width / 2;
396 setSize(QSize(width, height));
397 setFormat(Format::RGBA16F);
398 setHasTransparency(
false);
399 const int dataSize = width * height * 4 * 2;
400 QByteArray imageData;
401 imageData.resize(dataSize);
402 generateSkyTexture(width, height, imageData,
false);
403 setTextureData(imageData);
406QByteArray ProceduralSkyTextureData::generateSkyTexture(
int width,
int height, QByteArray &imageData,
bool isRGBE)
const
408 quint32 *data =
reinterpret_cast<quint32 *>(imageData.data());
410 LinearColor skyTopLinear(m_skyTopColor);
411 LinearColor skyHorizonLinear(m_skyHorizonColor);
412 LinearColor groundBottomLinear(m_groundBottomColor);
413 LinearColor groundHorizonLinear(m_groundHorizonColor);
414 LinearColor sunLinear(m_sunColor);
415 sunLinear.r *= m_sunEnergy;
416 sunLinear.g *= m_sunEnergy;
417 sunLinear.b *= m_sunEnergy;
419 QVector3D sun(0, 0, -1);
421 sun = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), m_sunLatitude) * sun;
422 sun = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), m_sunLongitude) * sun;
425 auto clamp = [](
float value,
float min,
float max) {
428 else if (value > max)
433 auto ease = [](
float x,
float c) {
440 return 1.0f - qPow(1.0f - x, 1.0f / c);
444 }
else if (c < 0.0f) {
446 return qPow(x * 2.0f, -c) * 0.5f;
448 return (1.0f - qPow(1.0f - (x - 0.5f) * 2.0f, -c)) * 0.5f + 0.5f;
454 for (
int i = 0; i < width; i++) {
456 float u =
float(i) / (width - 1);
457 float phi = u * 2.0 * M_PI;
459 for (
int j = 0; j < height; j++) {
460 float v =
float(j) / (height - 1);
461 float theta = v * M_PI;
463 QVector3D normal(qSin(phi) * qSin(theta) * -1.0,
465 qCos(phi) * qSin(theta) * -1.0);
467 float vAngle = qAcos(clamp(normal.y(), -1.0, 1.0));
470 if (normal.y() < 0) {
472 float c = (vAngle - (M_PI * 0.5f)) / (M_PI * 0.5f);
473 color = groundHorizonLinear.interpolate(groundBottomLinear, ease(c, m_groundCurve));
474 color.r *= m_groundEnergy;
475 color.g *= m_groundEnergy;
476 color.b *= m_groundEnergy;
479 float c = vAngle / (M_PI * 0.5f);
480 color = skyHorizonLinear.interpolate(skyTopLinear, ease(1.0 - c, m_skyCurve));
481 color.r *= m_skyEnergy;
482 color.g *= m_skyEnergy;
483 color.b *= m_skyEnergy;
485 float sunAngle = qRadiansToDegrees(qAcos(clamp(QVector3D::dotProduct(sun, normal), -1.0f, 1.0f)));
486 if (sunAngle < m_sunAngleMin) {
487 color = color.blend(sunLinear);
488 }
else if (sunAngle < m_sunAngleMax) {
489 float c2 = (sunAngle - m_sunAngleMin) / (m_sunAngleMax - m_sunAngleMin);
490 c2 = ease(c2, m_sunCurve);
491 color = color.blend(sunLinear).interpolate(color, c2);
497 data[(height - j - 1) * width + i] = color.toRGBE8();
500 const int offset = ((height - j - 1) * width + i) * 2;
501 qfloat16 *fData =
reinterpret_cast<qfloat16 *>(data + offset);
502 float pixel[4] = {color.r, color.g, color.b, color.a };
503 qFloatToFloat16(fData, pixel, 4);
511void ProceduralSkyTextureData::scheduleTextureUpdate()
513 generateRGBA16FTexture();
516ProceduralSkyTextureData::LinearColor::LinearColor(
const QColor &color)
518 const float red = color.redF();
519 const float green = color.greenF();
520 const float blue = color.blueF();
521 const float alpha = color.alphaF();
523 r = red < 0.04045 ? red * (1.0 / 12.92) : qPow((red + 0.055) * (1.0 / (1 + 0.055)), 2.4),
524 g = green < 0.04045 ? green * (1.0 / 12.92) : qPow((green + 0.055) * (1.0 / (1 + 0.055)), 2.4),
525 b = blue < 0.04045 ? blue * (1.0 / 12.92) : qPow((blue + 0.055) * (1.0 / (1 + 0.055)), 2.4),
529ProceduralSkyTextureData::LinearColor ProceduralSkyTextureData::LinearColor::interpolate(
const ProceduralSkyTextureData::LinearColor &color,
float value)
const
531 LinearColor copy = *
this;
533 copy.r += (value * (color.r - r));
534 copy.g += (value * (color.g - g));
535 copy.b += (value * (color.b - b));
536 copy.a += (value * (color.a - a));
541ProceduralSkyTextureData::LinearColor ProceduralSkyTextureData::LinearColor::blend(
const ProceduralSkyTextureData::LinearColor &color)
const
544 float sa = 1.0 - color.a;
545 copy.a = a * sa + color.a;
547 return LinearColor();
549 copy.r = (r * a * sa + color.r * color.a) / copy.a;
550 copy.g = (g * a * sa + color.g * color.a) / copy.a;
551 copy.b = (b * a * sa + color.b * color.a) / copy.a;
556quint32 ProceduralSkyTextureData::LinearColor::toRGBA8()
const
558 return (quint32(lrintf(r)) & 0xFF) |
559 ((quint32(lrintf(g)) & 0xFF) << 8) |
560 ((quint32(lrintf(b)) & 0xFF) << 16) |
561 ((quint32(lrintf(a)) & 0xFF) << 24);
564quint32 ProceduralSkyTextureData::LinearColor::toRGBE8()
const
575 v = frexp(v, &exp) * 256.0f / v;
577 quint8 *components =
reinterpret_cast<quint8*>(&result);
578 components[0] = quint8(r * v);
579 components[1] = quint8(g * v);
580 components[2] = quint8(b * v);
581 components[3] = quint8(exp + 128);
585quint32 ProceduralSkyTextureData::LinearColor::toRGBE9995()
const
587 const float pow2to9 = 512.0f;
588 const float B = 15.0f;
589 const float N = 9.0f;
591 float sharedExp = 65408.000f;
593 float cRed = qMax(0.0f, qMin(sharedExp, r));
594 float cGreen = qMax(0.0f, qMin(sharedExp, g));
595 float cBlue = qMax(0.0f, qMin(sharedExp, b));
597 float cMax = qMax(cRed, qMax(cGreen, cBlue));
599 float expp = qMax(-B - 1.0f, floor(std::log(cMax) / M_LN2)) + 1.0f + B;
601 float sMax = (
float)floor((cMax / qPow(2.0f, expp - B - N)) + 0.5f);
603 float exps = expp + 1.0f;
605 if (0.0 <= sMax && sMax < pow2to9) {
609 float sRed = qFloor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
610 float sGreen = qFloor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
611 float sBlue = qFloor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
613 return (quint32(lrintf(sRed)) & 0x1FF) |
614 ((quint32(lrintf(sGreen)) & 0x1FF) << 9) |
615 ((quint32(lrintf(sBlue)) & 0x1FF) << 18) |
616 ((quint32(lrintf(exps)) & 0x1F) << 27);
Combined button and popup list for selecting options.