1+ #version 430 core
2+
3+ // Copyright (C) 2018-2024 - DevSH Graphics Programming Sp. z O.O.
4+ // This file is part of the "Nabla Engine".
5+ // For conditions of distribution and use, see copyright notice in nabla.h
6+
7+ #include "common.h"
8+
9+ layout(local_size_x = WORKGROUP_DIMENSION, local_size_y = WORKGROUP_DIMENSION) in;
10+
11+ layout(set = 0, binding = 0, r16) restrict uniform image2D outIESCandelaImage;
12+ layout(set = 0, binding = 1, rg32f) restrict uniform image2D outSphericalCoordinatesImage;
13+ layout(set = 0, binding = 2, rgba32f) restrict uniform image2D outOUVProjectionDirectionImage;
14+ layout(set = 0, binding = 3, rg8) restrict uniform image2D outPassTMask;
15+
16+ layout(std430, set = 0, binding = 4) readonly buffer HorizontalAngles
17+ {
18+ double hAngles[];
19+ };
20+
21+ layout(std430, set = 0, binding = 5) readonly buffer VerticalAngles
22+ {
23+ double vAngles[];
24+ };
25+
26+ layout(std430, set = 0, binding = 6) readonly buffer Data
27+ {
28+ double data[];
29+ };
30+
31+ layout(push_constant) uniform PushConstants
32+ {
33+ float maxIValue;
34+ float zAngleDegreeRotation;
35+ uint mode;
36+ uint dummy;
37+ } pc;
38+
39+ vec3 octahedronUVToDir(vec2 uv)
40+ {
41+ vec3 position = vec3((uv * 2.0 - 1.0).xy, 0.0);
42+ vec2 absP = vec2(abs(position.x), abs(position.y));
43+
44+ position.z = 1.0 - absP.x - absP.y;
45+
46+ if (position.z < 0.0)
47+ {
48+ position.x = sign(position.x) * (1.0 - absP.y);
49+ position.y = sign(position.y) * (1.0 - absP.x);
50+ }
51+
52+ // rotate position vector around Z-axis with "pc.zAngleDegreeRotation"
53+ if(pc.zAngleDegreeRotation != 0.0)
54+ {
55+ float rDegree = pc.zAngleDegreeRotation;
56+
57+ const float zAngleRadians = float(rDegree * M_PI / 180.0);
58+ const float cosineV = cos(zAngleRadians);
59+ const float sineV = sin(zAngleRadians);
60+
61+ position = vec3(cosineV * position.x - cosineV * position.y, sineV * position.x + sineV * position.y, position.z);
62+ //position = vec3((cosineV * position.x) - (sineV * position.y), (cosineV * position.x) + (sineV * position.y), position.z);
63+ }
64+
65+ return normalize(position);
66+ }
67+
68+ //! Returns spherical coordinates with physics convention in radians
69+ /*
70+ https://en.wikipedia.org/wiki/Spherical_coordinate_system#/media/File:3D_Spherical.svg
71+ Retval.x is "theta" polar angle in range [0, PI] & Retval.y "phi" is azimuthal angle
72+ in range [-PI, PI] range
73+ */
74+
75+ vec2 sphericalDirToRadians(vec3 direction)
76+ {
77+ double theta = acos(clamp(direction.z/length(direction), -1.0, 1.0));
78+ double phi = atan(direction.y, direction.x);
79+
80+ return vec2(theta, phi);
81+ }
82+
83+ uint implGetVUB(const float angle)
84+ {
85+ const uint len = vAngles.length();
86+
87+ for(uint i = 0; i < len; ++i)
88+ if(vAngles[i] > angle)
89+ return i;
90+
91+ return len;
92+ }
93+
94+ uint implGetHUB(const float angle)
95+ {
96+ const uint len = hAngles.length();
97+
98+ for(uint i = 0; i < len; ++i)
99+ if(hAngles[i] > angle)
100+ return i;
101+
102+ return len;
103+ }
104+
105+ uint getVLB(const float angle)
106+ {
107+ return uint(max(int(implGetVUB(angle)) - 1, 0));
108+ }
109+
110+ uint getHLB(const float angle)
111+ {
112+ return uint(max(int(implGetHUB(angle)) - 1, 0));
113+ }
114+
115+ uint getVUB(const float angle)
116+ {
117+ return uint(min(int(implGetVUB(angle)), int(vAngles.length()) - 1));
118+ }
119+
120+ uint getHUB(const float angle)
121+ {
122+ return uint(min(int(implGetHUB(angle)), int(hAngles.length()) - 1));
123+ }
124+
125+ double getValue(uint i, uint j)
126+ {
127+ return data[vAngles.length() * i + j];
128+ }
129+
130+ // symmetry
131+ #define ISOTROPIC 0u
132+ #define QUAD_SYMETRIC 1u
133+ #define HALF_SYMETRIC 2u
134+ #define NO_LATERAL_SYMMET 3u
135+
136+ uint getSymmetry() // TODO: to reduce check time we could pass it with PCs
137+ {
138+ const uint hALength = hAngles.length();
139+ if(hALength < 2) // careful here, somebody can break it by feeding us with too much data by mistake
140+ return ISOTROPIC;
141+
142+ const double hABack = hAngles[hALength - 1];
143+
144+ if(hABack == 90)
145+ return QUAD_SYMETRIC;
146+ else if(hABack == 180) // note that OTHER_HALF_SYMMETRIC = HALF_SYMETRIC here
147+ return HALF_SYMETRIC;
148+ else
149+ return NO_LATERAL_SYMMET;
150+ }
151+
152+ float wrapPhi(const float phi, const uint symmetry) //! wrap phi spherical coordinate compoment to range defined by symmetry
153+ {
154+ switch (symmetry)
155+ {
156+ case ISOTROPIC:
157+ return 0.0;
158+ case QUAD_SYMETRIC: //! phi MIRROR_REPEAT wrap onto [0, 90] degrees range
159+ {
160+ float wrapPhi = abs(phi); //! first MIRROR
161+
162+ if(wrapPhi > M_HALF_PI) //! then REPEAT
163+ wrapPhi = clamp(M_HALF_PI - (wrapPhi - M_HALF_PI), 0, M_HALF_PI);
164+
165+ return wrapPhi; //! eg. maps (in degrees) 91,269,271 -> 89 and 179,181,359 -> 1
166+ }
167+ case HALF_SYMETRIC: //! phi MIRROR wrap onto [0, 180] degrees range
168+ return abs(phi); //! eg. maps (in degress) 181 -> 179 or 359 -> 1
169+ case NO_LATERAL_SYMMET:
170+ {
171+ if(phi < 0)
172+ return phi + 2.0 * M_PI;
173+ else
174+ return phi;
175+ }
176+ }
177+
178+ return 69;
179+ }
180+
181+ double sampleI(const vec2 sphericalCoordinates, const uint symmetry)
182+ {
183+ const float vAngle = degrees(sphericalCoordinates.x), hAngle = degrees(wrapPhi(sphericalCoordinates.y, symmetry));
184+
185+ double vABack = vAngles[vAngles.length() - 1];
186+ double hABack = hAngles[hAngles.length() - 1];
187+
188+ if (vAngle > vABack)
189+ return 0.0;
190+
191+ // bilinear interpolation
192+ uint j0 = getVLB(vAngle);
193+ uint j1 = getVUB(vAngle);
194+ uint i0 = symmetry == ISOTROPIC ? 0 : getHLB(hAngle);
195+ uint i1 = symmetry == ISOTROPIC ? 0 : getHUB(hAngle);
196+
197+ double uReciprocal = i1 == i0 ? 1.0 : 1.0 / (hAngles[i1] - hAngles[i0]);
198+ double vReciprocal = j1 == j0 ? 1.0 : 1.0 / (vAngles[j1] - vAngles[j0]);
199+
200+ double u = (hAngle - hAngles[i0]) * uReciprocal;
201+ double v = (vAngle - vAngles[j0]) * vReciprocal;
202+
203+ double s0 = getValue(i0, j0) * (1.0 - v) + getValue(i0, j1) * (v);
204+ double s1 = getValue(i1, j0) * (1.0 - v) + getValue(i1, j1) * (v);
205+
206+ return s0 * (1.0 - u) + s1 * u;
207+ }
208+
209+ //! Checks if (x,y) /in [0,PI] x [-PI,PI] product
210+ /*
211+ IES vertical range is [0, 180] degrees
212+ and horizontal range is [0, 360] degrees
213+ but for easier computations (MIRROR & MIRROW_REPEAT operations)
214+ we represent horizontal range as [-180, 180] given spherical coordinates
215+ */
216+
217+ bool isWithinSCDomain(vec2 point)
218+ {
219+ const vec2 lb = vec2(0, -M_PI);
220+ const vec2 ub = vec2(M_PI, M_PI);
221+
222+ return all(lessThanEqual(lb, point)) && all(lessThanEqual(point, ub));
223+ }
224+
225+ void main()
226+ {
227+ const float VERTICAL_INVERSE = 1.0f / TEXTURE_SIZE;
228+ const float HORIZONTAL_INVERSE = 1.0f / TEXTURE_SIZE;
229+
230+ const ivec2 pixelCoordinates = ivec2(gl_GlobalInvocationID.xy);
231+ const ivec2 destinationSize = imageSize(outIESCandelaImage);
232+
233+ if (all(lessThan(pixelCoordinates, destinationSize)))
234+ {
235+ const vec2 uv = vec2((float(pixelCoordinates.x) + 0.5) * VERTICAL_INVERSE, (float(pixelCoordinates.y) + 0.5) * HORIZONTAL_INVERSE);
236+ const vec3 direction = octahedronUVToDir(uv);
237+ const vec2 sphericalCoordinates = sphericalDirToRadians(direction); // third radius spherical compoment is normalized and skipped
238+
239+ const double intensity = sampleI(sphericalCoordinates, getSymmetry());
240+ const vec4 value = vec4(intensity / pc.maxIValue, 0, 0, 0);
241+
242+ const double normD = length(direction);
243+ vec2 mask;
244+
245+ if(1.0 - QUANT_ERROR_ADMISSIBLE <= normD && normD <= 1.0 + QUANT_ERROR_ADMISSIBLE)
246+ mask.x = 1.0; // pass
247+ else
248+ mask.x = 0;
249+
250+ if(isWithinSCDomain(sphericalCoordinates))
251+ mask.y = 1.0; // pass
252+ else
253+ mask.y = 0;
254+
255+ imageStore(outIESCandelaImage, pixelCoordinates, value);
256+ imageStore(outSphericalCoordinatesImage, pixelCoordinates, vec4(sphericalCoordinates, 0, 1));
257+ imageStore(outOUVProjectionDirectionImage, pixelCoordinates, vec4(direction.xyz, 1));
258+ imageStore(outPassTMask, pixelCoordinates, vec4(mask.xy, 1, 1));
259+ }
260+ }
0 commit comments