Skip to content

Commit 1e9138e

Browse files
author
devsh
committed
make the IES octahedral maps corner sampled
1 parent 07feaff commit 1e9138e

File tree

4 files changed

+36
-21
lines changed

4 files changed

+36
-21
lines changed

examples_tests

include/nbl/builtin/glsl/ies/functions.glsl

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,33 @@
66

77
#include <nbl/builtin/glsl/math/constants.glsl>
88

9-
// TODO: implement proper mirroing
10-
// MIRROR_180_BITS = 0b001, Last Angle is 180, so map V slightly differently
11-
// MIRROR_90_BITS = 0b010, Last Angle is 90, so map both U and V slightly differently
12-
// ISOTROPIC_BITS = 0b011, texture to sample is Nx1, pretend v=middle always
13-
// FULL_THETA_BIT = 0b100, handle extended domain and rotate by 45 degrees for anisotropic
14-
15-
vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir) {
16-
float sum = dot(vec3(1.0f), abs(dir));
9+
// TODO: when rewriting to HLSL this is not IES namespace or folder, this should be octahedral mapping sitting somewhere where the spherical/polar sits
10+
// NOTE: I changed it to return NDC [-1,1]^2 instead of UV coords [0,1]^2
11+
vec2 nbl_glsl_TODOnamespace_octahedral_mapping(vec3 dir)
12+
{
13+
float sum = dot(vec3(1.0f), abs(dir));
1714
vec3 s = dir / sum;
1815

19-
if(s.z < 0.0f) {
20-
s.xy = sign(s.xy) * (1.0f - abs(s.yx));
16+
if(s.z < 0.0f)
17+
{
18+
const uvec2 flipSignMask = floatBitsToUint(s.xy)&0x80000000u;
19+
s.xy = uintBitsToFloat(floatBitsToUint(1.0f - abs(s.yx))^flipSignMask);
2120
}
2221

23-
return s.xy * 0.5f + 0.5f;
22+
return s.xy;
2423
}
2524

26-
// vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir) {
27-
// return vec2((atan(dir.x, dir.y) + nbl_glsl_PI) / (2.0*nbl_glsl_PI), acos(dir.z) / nbl_glsl_PI);
28-
// }
25+
// TODO: implement proper mirroing
26+
// MIRROR_180_BITS = 0b001, Last Angle is 180, so map V with MIRROR and corner sampling off
27+
// MIRROR_90_BITS = 0b010, Last Angle is 90, so map both U and V with MIRROR and corner sampling off
28+
// ISOTROPIC_BITS = 0b011, texture to sample is Nx1, pretend v=middle always , and make u REPEAT or CLAMP_TO_BORDER
29+
// FULL_THETA_BIT = 0b100, handle truncated domain and rotate by 45 degrees for anisotropic
30+
// (certain combos wont work like 90 degree 2 symmetry domain & half theta), it really needs to be an 8 case label thing explicitly enumerated
31+
vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir, vec2 halfMinusHalfPixel)
32+
{
33+
// halfMinusHalfPixel = 0.5-0.5/texSize
34+
// believe it or not, cornerSampled(NDC*0.5+0.5) = NDC*0.5*(1-1/texSize)+0.5
35+
return nbl_glsl_TODOnamespace_octahedral_mapping(dir)*halfMinusHalfPixel+0.5;
36+
}
2937

3038
#endif

include/nbl/builtin/glsl/material_compiler/common.glsl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,8 @@ vec3 nbl_glsl_MC_oriented_material_t_getEmissive(in nbl_glsl_MC_oriented_materia
601601
if ((floatBitsToInt(emitter.orientation[0])&1u) != 1u) {
602602
right *= -1;
603603
}
604-
return emissive * nbl_glsl_vTextureGrad(emitter.emissionProfile, nbl_glsl_IES_convert_dir_to_uv(mat3(right, up, view)*dir), mat2(0.0)).r;
604+
vec2 halfMinusHalfPixel = 0.5-0.5/vec2(nbl_glsl_unpackSize(emitter.emissionProfile));
605+
return emissive * nbl_glsl_vTextureGrad(emitter.emissionProfile, nbl_glsl_IES_convert_dir_to_uv(mat3(right, up, view)*dir,halfMinusHalfPixel), mat2(0.0)).r;
605606
}
606607
#endif
607608
return emissive;

src/nbl/asset/utils/CIESProfile.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ inline core::vectorSIMDf CIESProfile::octahdronUVToDir(const float& u, const flo
8888
float abs_x = core::abs(pos.x), abs_y = core::abs(pos.y);
8989
pos.z = 1.0 - abs_x - abs_y;
9090
if (pos.z < 0.0) {
91-
pos.x = core::sign(pos.x) * (1.0 - abs_y);
92-
pos.y = core::sign(pos.y) * (1.0 - abs_x);
91+
pos.x = (pos.x<0.f ? (-1.f):1.f) * (1.0 - abs_y);
92+
pos.y = (pos.y<0.f ? (-1.f):1.f) * (1.0 - abs_x);
9393
}
9494

9595
return core::normalize(pos);
@@ -116,6 +116,8 @@ core::smart_refctd_ptr<asset::ICPUImageView> CIESProfile::createIESTexture(Execu
116116
if (height > CDC_MAX_TEXTURE_HEIGHT)
117117
height = CDC_MAX_TEXTURE_HEIGHT;
118118

119+
// TODO: If no symmetry (no folding in half and abuse of mirror sampler) make dimensions odd-sized so middle texel taps the south pole
120+
119121
asset::ICPUImage::SCreationParams imgInfo;
120122
imgInfo.type = asset::ICPUImage::ET_2D;
121123
imgInfo.extent.width = width;
@@ -161,15 +163,19 @@ core::smart_refctd_ptr<asset::ICPUImageView> CIESProfile::createIESTexture(Execu
161163
const double maxValue = getMaxCandelaValue();
162164
const double maxValueRecip = 1.0 / maxValue;
163165

164-
const double vertInv = 1.0 / height;
165-
const double horiInv = 1.0 / width;
166+
// There is one huge issue, the IES files love to give us values for degrees 0, 90, 180 an 360
167+
// So standard octahedral mapping won't work, because for above data points you need corner sampled images.
168+
const float vertInv = 1.0 / (height-1);
169+
const float horiInv = 1.0 / (width-1);
166170

167171
const double flattenTarget = getAvgEmmision(fullDomainFlatten);
168172
const double domainLo = core::radians(vAngles.front());
169173
const double domainHi = core::radians(vAngles.back());
170174
auto fill = [&](uint32_t blockArrayOffset, core::vectorSIMDu32 position) -> void
171175
{
172-
const auto dir = octahdronUVToDir(((float)position.x + 0.5) * vertInv, ((float)position.y + 0.5) * horiInv);
176+
// We don't currently support generating IES images that exploit symmetries or reduced domains, all are full octahederal mappings of a sphere.
177+
// If we did, we'd rely on MIRROR and CLAMP samplers to do some of the work for us while handling the discontinuity due to corner sampling.
178+
const auto dir = octahdronUVToDir(position.x * vertInv, position.y * horiInv);
173179
const auto [theta, phi] = sphericalDirToRadians(dir);
174180
const auto intensity = sample(theta, phi);
175181

0 commit comments

Comments
 (0)