scene/records/lights.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #pragma once | ||
| 3 | |||
| 4 | #include "chrono_batch_render/io/kernels/ies.h" | ||
| 5 | #include "chrono_batch_render/scene/records/instances.h" | ||
| 6 | #include "chrono_batch_render/scene/records/textures.h" | ||
| 7 | #include "chrono_batch_render/stage/schema/ids.h" | ||
| 8 | |||
| 9 | namespace chrono::batch_render { | ||
| 10 | |||
| 11 | CHRONO_BATCH_RENDER_ENUM(LightType) { | ||
| 12 | Distant, | ||
| 13 | Sphere, | ||
| 14 | Rect, | ||
| 15 | Disk, | ||
| 16 | Cylinder, | ||
| 17 | Dome, | ||
| 18 | Portal, | ||
| 19 | Unsupported, | ||
| 20 | }; | ||
| 21 | |||
| 22 | static const uint kLightTreatAsPoint = 1u << 0; | ||
| 23 | static const uint kLightTreatAsLine = 1u << 1; | ||
| 24 | static const uint kLightNormalize = 1u << 2; | ||
| 25 | |||
| 26 | static const uint kLightShaped = 1u << 3; | ||
| 27 | static const uint kLightVisible = 1u << 4; | ||
| 28 | static const uint kLightShadowless = 1u << 5; | ||
| 29 | |||
| 30 | struct LightRecord { | ||
| 31 | Float3 position; | ||
| 32 | uint prim; | ||
| 33 | Float3 axisX; | ||
| 34 | uint type; | ||
| 35 | Float3 axisY; | ||
| 36 | uint flags; | ||
| 37 | Float3 axisZ; | ||
| 38 | float power; | ||
| 39 | Float3 radiance; | ||
| 40 | float radius; | ||
| 41 | Float3 focusTint; | ||
| 42 | float focus; | ||
| 43 | float width; | ||
| 44 | float height; | ||
| 45 | float length; | ||
| 46 | float cosAngle; | ||
| 47 | float diffuse; | ||
| 48 | float specular; | ||
| 49 | float coneAngle; | ||
| 50 | float coneSoftness; | ||
| 51 | uint forwardAxis; | ||
| 52 | float spread; | ||
| 53 | uint lightLink; | ||
| 54 | uint shadowLink; | ||
| 55 | Float3 shadowColor; | ||
| 56 | float shadowDistance; | ||
| 57 | float shadowFalloff; | ||
| 58 | float shadowFalloffGamma; | ||
| 59 | uint textures; | ||
| 60 | uint textureFormat; | ||
| 61 | Ptr<float> map; | ||
| 62 | uint portals; | ||
| 63 | uint ies; | ||
| 64 | float iesAngleScale; | ||
| 65 | float iesScale; | ||
| 66 | uint instancer; | ||
| 67 | uint copyOffset; | ||
| 68 | uint64_t portalPrims; | ||
| 69 | }; | ||
| 70 | |||
| 71 | CHRONO_BATCH_RENDER_CHECK_SIZE(LightRecord, 216); | ||
| 72 | |||
| 73 | struct LightLayout { | ||
| 74 | Ptr<LightRecord> records; | ||
| 75 | Ptr<uint> firstLights; | ||
| 76 | Ptr<uint> prims; | ||
| 77 | Ptr<float> cdf; | ||
| 78 | Ptr<float> maps; | ||
| 79 | Ptr<uint> domePrims; | ||
| 80 | Ptr<uint> domeSlots; | ||
| 81 | Ptr<uint> offsetBases; | ||
| 82 | Ptr<Float3> offsets; | ||
| 83 | Ptr<uint> copies; | ||
| 84 | uint worldCount; | ||
| 85 | uint domeCount; | ||
| 86 | }; | ||
| 87 | |||
| 88 | CHRONO_BATCH_RENDER_CHECK_SIZE(LightLayout, 88); | ||
| 89 | |||
| 90 | 11 | struct MinimalLightRecord { | |
| 91 | Float3 direction; | ||
| 92 | uint headlight; | ||
| 93 | Float3 radiance; | ||
| 94 | float diffuse; | ||
| 95 | float specular; | ||
| 96 | uint padding0; | ||
| 97 | uint padding1; | ||
| 98 | uint padding2; | ||
| 99 | }; | ||
| 100 | |||
| 101 | CHRONO_BATCH_RENDER_CHECK_SIZE(MinimalLightRecord, 48); | ||
| 102 | |||
| 103 | static const uint kDomeMapWidth = 256; | ||
| 104 | static const uint kDomeMapHeight = 128; | ||
| 105 | static const uint kDomeMapCells = kDomeMapWidth * kDomeMapHeight; | ||
| 106 | static const uint kDomeMapFloats = kDomeMapCells + kDomeMapHeight + 1; | ||
| 107 | |||
| 108 | static const uint kSpectralRows = 95; | ||
| 109 | static const float kSpectralFirstNm = 360; | ||
| 110 | static const float kSpectralStepNm = 5; | ||
| 111 | static const uint kTemperatureRows = 256; | ||
| 112 | static const float kMaxMired = 1000; | ||
| 113 | static const uint kBlackbodyRow = kSpectralRows; | ||
| 114 | static const uint kDaylightRow = kBlackbodyRow + kTemperatureRows; | ||
| 115 | static const uint kWhiteRow = kDaylightRow + kTemperatureRows; | ||
| 116 | |||
| 117 | #ifndef __SLANG__ | ||
| 118 | |||
| 119 | struct SpectralTables { | ||
| 120 | Float3 cmf[kSpectralRows]; | ||
| 121 | Float3 blackbody[kTemperatureRows]; | ||
| 122 | Float3 daylight[kTemperatureRows]; | ||
| 123 | Float3 white; | ||
| 124 | uint padding; | ||
| 125 | }; | ||
| 126 | |||
| 127 | CHRONO_BATCH_RENDER_CHECK_SIZE(SpectralTables, 7300); | ||
| 128 | |||
| 129 | #endif | ||
| 130 | |||
| 131 | struct CollectionRecord { | ||
| 132 | ArrayRef includes; | ||
| 133 | ArrayRef excludes; | ||
| 134 | uint includeRoot; | ||
| 135 | uint explicitOnly; | ||
| 136 | uint padding0; | ||
| 137 | uint padding1; | ||
| 138 | }; | ||
| 139 | |||
| 140 | CHRONO_BATCH_RENDER_CHECK_SIZE(CollectionRecord, 48); | ||
| 141 | |||
| 142 | struct LightShapeRecord { | ||
| 143 | float radius; | ||
| 144 | float width; | ||
| 145 | float height; | ||
| 146 | float length; | ||
| 147 | uint treatAsPoint; | ||
| 148 | uint padding0; | ||
| 149 | uint padding1; | ||
| 150 | uint padding2; | ||
| 151 | }; | ||
| 152 | |||
| 153 | CHRONO_BATCH_RENDER_CHECK_SIZE(LightShapeRecord, 32); | ||
| 154 | |||
| 155 | static const uint kLoopedLights = 8; | ||
| 156 | |||
| 157 | #ifdef __SLANG__ | ||
| 158 | |||
| 159 | bool Lists(ArrayRef targets, uint prim) { | ||
| 160 | Ptr<uint> items = Ptr<uint>(targets.address); | ||
| 161 | for (uint i = 0; i < targets.count; ++i) { | ||
| 162 | if (items[i] == prim) { | ||
| 163 | return true; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | return false; | ||
| 167 | } | ||
| 168 | |||
| 169 | float3 ShapingWeight(LightRecord light, float cosine) { | ||
| 170 | if ((light.flags & kLightShaped) == 0) { | ||
| 171 | return float3(1); | ||
| 172 | } | ||
| 173 | const float angle = acos(clamp(cosine, -1.0f, 1.0f)); | ||
| 174 | float cone = angle <= light.coneAngle ? 1 : 0; | ||
| 175 | const float inner = light.coneAngle * (1 - light.coneSoftness); | ||
| 176 | if (light.coneSoftness > 0 && angle > inner && angle <= light.coneAngle) { | ||
| 177 | cone = smoothstep(light.coneAngle, inner, angle); | ||
| 178 | } | ||
| 179 | const float focused = light.focus > 0 ? pow(max(cosine, 0.0f), light.focus) : 1; | ||
| 180 | const float3 tint = light.focusTint.Vector(); | ||
| 181 | return cone * (tint + (1 - tint) * focused); | ||
| 182 | } | ||
| 183 | |||
| 184 | float3 ForwardAxis(LightRecord light) { | ||
| 185 | switch (ShapingAxis(light.forwardAxis)) { | ||
| 186 | case ShapingAxis::PlusZ: | ||
| 187 | return light.axisZ.Vector(); | ||
| 188 | case ShapingAxis::MinusY: | ||
| 189 | return -light.axisY.Vector(); | ||
| 190 | case ShapingAxis::PlusY: | ||
| 191 | return light.axisY.Vector(); | ||
| 192 | case ShapingAxis::MinusX: | ||
| 193 | return -light.axisX.Vector(); | ||
| 194 | case ShapingAxis::PlusX: | ||
| 195 | return light.axisX.Vector(); | ||
| 196 | default: | ||
| 197 | return -light.axisZ.Vector(); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | float3 EmissionAcross(LightRecord light) { | ||
| 202 | const ShapingAxis axis = ShapingAxis(light.forwardAxis); | ||
| 203 | return axis == ShapingAxis::MinusX || axis == ShapingAxis::PlusX ? light.axisY.Vector() | ||
| 204 | : light.axisX.Vector(); | ||
| 205 | } | ||
| 206 | |||
| 207 | float2 IesSt(LightRecord light, float3 emitted) { | ||
| 208 | const float3 forward = ForwardAxis(light); | ||
| 209 | const float3 across = EmissionAcross(light); | ||
| 210 | const float polar = | ||
| 211 | acos(clamp(dot(emitted, forward), -1.0f, 1.0f)) / max(1 + light.iesAngleScale, 1e-3f); | ||
| 212 | if (polar > kPi) { | ||
| 213 | return float2(-1); | ||
| 214 | } | ||
| 215 | float around = atan2(dot(emitted, cross(forward, across)), dot(emitted, across)) / (2 * kPi); | ||
| 216 | around -= floor(around); | ||
| 217 | return float2((around * (kIesColumns - 1) + 0.5f) / kIesColumns, | ||
| 218 | 1 - (polar / kPi * (kIesRows - 1) + 0.5f) / kIesRows); | ||
| 219 | } | ||
| 220 | |||
| 221 | float2 CrossFace(uint column, uint row, float a, float b) { | ||
| 222 | const float edge = 1 - 1e-4f; | ||
| 223 | const float2 at = float2(clamp(a, -edge, edge), clamp(b, -edge, edge)); | ||
| 224 | return float2((column + 0.5f * (at.x + 1)) / 3, 1 - (row + 0.5f * (1 - at.y)) / 4); | ||
| 225 | } | ||
| 226 | |||
| 227 | float2 DomeTextureSt(DomeTextureFormat format, float3 n) { | ||
| 228 | switch (format) { | ||
| 229 | case DomeTextureFormat::MirroredBall: { | ||
| 230 | const float3 h = n + float3(0, 0, 1); | ||
| 231 | const float size = length(h); | ||
| 232 | const float2 xy = size > 1e-6f ? h.xy / size : float2(1, 0); | ||
| 233 | return 0.5f + 0.5f * xy; | ||
| 234 | } | ||
| 235 | case DomeTextureFormat::Angular: { | ||
| 236 | const float side = length(n.xy); | ||
| 237 | const float radius = side > 1e-6f ? acos(clamp(n.z, -1.0f, 1.0f)) / (kPi * side) : 0; | ||
| 238 | return 0.5f + 0.5f * radius * n.xy; | ||
| 239 | } | ||
| 240 | case DomeTextureFormat::CubeMapVerticalCross: { | ||
| 241 | const float3 a = abs(n); | ||
| 242 | if (a.x >= a.y && a.x >= a.z) { | ||
| 243 | return n.x > 0 ? CrossFace(2, 1, n.z / a.x, n.y / a.x) | ||
| 244 | : CrossFace(0, 1, -n.z / a.x, n.y / a.x); | ||
| 245 | } | ||
| 246 | if (a.y >= a.z) { | ||
| 247 | return n.y > 0 ? CrossFace(1, 0, n.x / a.y, n.z / a.y) | ||
| 248 | : CrossFace(1, 2, n.x / a.y, -n.z / a.y); | ||
| 249 | } | ||
| 250 | return n.z < 0 ? CrossFace(1, 1, n.x / a.z, n.y / a.z) | ||
| 251 | : CrossFace(1, 3, n.x / a.z, -n.y / a.z); | ||
| 252 | } | ||
| 253 | default: { | ||
| 254 | const float u = (atan2(n.z, n.x) + 0.5f * kPi) / (2 * kPi); | ||
| 255 | return float2(u - floor(u), 1 - acos(clamp(n.y, -1.0f, 1.0f)) / kPi); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | float3 DomeMapDirection(float2 uv) { | ||
| 261 | const float phi = 2 * kPi * uv.x; | ||
| 262 | const float theta = kPi * uv.y; | ||
| 263 | return float3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi)); | ||
| 264 | } | ||
| 265 | |||
| 266 | #endif | ||
| 267 | |||
| 268 | } | ||
| 269 |