Spaces:
Running
on
T4
Running
on
T4
File size: 12,022 Bytes
b4c8bc3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
"""Punctual light sources as defined by the glTF 2.0 KHR extension at
https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
Author: Matthew Matl
"""
import abc
import numpy as np
import six
from OpenGL.GL import *
from .utils import format_color_vector
from .texture import Texture
from .constants import SHADOW_TEX_SZ
from .camera import OrthographicCamera, PerspectiveCamera
@six.add_metaclass(abc.ABCMeta)
class Light(object):
"""Base class for all light objects.
Parameters
----------
color : (3,) float
RGB value for the light's color in linear space.
intensity : float
Brightness of light. The units that this is defined in depend on the
type of light. Point and spot lights use luminous intensity in candela
(lm/sr), while directional lights use illuminance in lux (lm/m2).
name : str, optional
Name of the light.
"""
def __init__(self,
color=None,
intensity=None,
name=None):
if color is None:
color = np.ones(3)
if intensity is None:
intensity = 1.0
self.name = name
self.color = color
self.intensity = intensity
self._shadow_camera = None
self._shadow_texture = None
@property
def name(self):
"""str : The user-defined name of this object.
"""
return self._name
@name.setter
def name(self, value):
if value is not None:
value = str(value)
self._name = value
@property
def color(self):
"""(3,) float : The light's color.
"""
return self._color
@color.setter
def color(self, value):
self._color = format_color_vector(value, 3)
@property
def intensity(self):
"""float : The light's intensity in candela or lux.
"""
return self._intensity
@intensity.setter
def intensity(self, value):
self._intensity = float(value)
@property
def shadow_texture(self):
""":class:`.Texture` : A texture used to hold shadow maps for this light.
"""
return self._shadow_texture
@shadow_texture.setter
def shadow_texture(self, value):
if self._shadow_texture is not None:
if self._shadow_texture._in_context():
self._shadow_texture.delete()
self._shadow_texture = value
@abc.abstractmethod
def _generate_shadow_texture(self, size=None):
"""Generate a shadow texture for this light.
Parameters
----------
size : int, optional
Size of texture map. Must be a positive power of two.
"""
pass
@abc.abstractmethod
def _get_shadow_camera(self, scene_scale):
"""Generate and return a shadow mapping camera for this light.
Parameters
----------
scene_scale : float
Length of scene's bounding box diagonal.
Returns
-------
camera : :class:`.Camera`
The camera used to render shadowmaps for this light.
"""
pass
class DirectionalLight(Light):
"""Directional lights are light sources that act as though they are
infinitely far away and emit light in the direction of the local -z axis.
This light type inherits the orientation of the node that it belongs to;
position and scale are ignored except for their effect on the inherited
node orientation. Because it is at an infinite distance, the light is
not attenuated. Its intensity is defined in lumens per metre squared,
or lux (lm/m2).
Parameters
----------
color : (3,) float, optional
RGB value for the light's color in linear space. Defaults to white
(i.e. [1.0, 1.0, 1.0]).
intensity : float, optional
Brightness of light, in lux (lm/m^2). Defaults to 1.0
name : str, optional
Name of the light.
"""
def __init__(self,
color=None,
intensity=None,
name=None):
super(DirectionalLight, self).__init__(
color=color,
intensity=intensity,
name=name,
)
def _generate_shadow_texture(self, size=None):
"""Generate a shadow texture for this light.
Parameters
----------
size : int, optional
Size of texture map. Must be a positive power of two.
"""
if size is None:
size = SHADOW_TEX_SZ
self.shadow_texture = Texture(width=size, height=size,
source_channels='D', data_format=GL_FLOAT)
def _get_shadow_camera(self, scene_scale):
"""Generate and return a shadow mapping camera for this light.
Parameters
----------
scene_scale : float
Length of scene's bounding box diagonal.
Returns
-------
camera : :class:`.Camera`
The camera used to render shadowmaps for this light.
"""
return OrthographicCamera(
znear=0.01 * scene_scale,
zfar=10 * scene_scale,
xmag=scene_scale,
ymag=scene_scale
)
class PointLight(Light):
"""Point lights emit light in all directions from their position in space;
rotation and scale are ignored except for their effect on the inherited
node position. The brightness of the light attenuates in a physically
correct manner as distance increases from the light's position (i.e.
brightness goes like the inverse square of the distance). Point light
intensity is defined in candela, which is lumens per square radian (lm/sr).
Parameters
----------
color : (3,) float
RGB value for the light's color in linear space.
intensity : float
Brightness of light in candela (lm/sr).
range : float
Cutoff distance at which light's intensity may be considered to
have reached zero. If None, the range is assumed to be infinite.
name : str, optional
Name of the light.
"""
def __init__(self,
color=None,
intensity=None,
range=None,
name=None):
super(PointLight, self).__init__(
color=color,
intensity=intensity,
name=name,
)
self.range = range
@property
def range(self):
"""float : The cutoff distance for the light.
"""
return self._range
@range.setter
def range(self, value):
if value is not None:
value = float(value)
if value <= 0:
raise ValueError('Range must be > 0')
self._range = value
self._range = value
def _generate_shadow_texture(self, size=None):
"""Generate a shadow texture for this light.
Parameters
----------
size : int, optional
Size of texture map. Must be a positive power of two.
"""
raise NotImplementedError('Shadows not implemented for point lights')
def _get_shadow_camera(self, scene_scale):
"""Generate and return a shadow mapping camera for this light.
Parameters
----------
scene_scale : float
Length of scene's bounding box diagonal.
Returns
-------
camera : :class:`.Camera`
The camera used to render shadowmaps for this light.
"""
raise NotImplementedError('Shadows not implemented for point lights')
class SpotLight(Light):
"""Spot lights emit light in a cone in the direction of the local -z axis.
The angle and falloff of the cone is defined using two numbers, the
``innerConeAngle`` and ``outerConeAngle``.
As with point lights, the brightness
also attenuates in a physically correct manner as distance increases from
the light's position (i.e. brightness goes like the inverse square of the
distance). Spot light intensity refers to the brightness inside the
``innerConeAngle`` (and at the location of the light) and is defined in
candela, which is lumens per square radian (lm/sr). A spot light's position
and orientation are inherited from its node transform. Inherited scale does
not affect cone shape, and is ignored except for its effect on position
and orientation.
Parameters
----------
color : (3,) float
RGB value for the light's color in linear space.
intensity : float
Brightness of light in candela (lm/sr).
range : float
Cutoff distance at which light's intensity may be considered to
have reached zero. If None, the range is assumed to be infinite.
innerConeAngle : float
Angle, in radians, from centre of spotlight where falloff begins.
Must be greater than or equal to ``0`` and less
than ``outerConeAngle``. Defaults to ``0``.
outerConeAngle : float
Angle, in radians, from centre of spotlight where falloff ends.
Must be greater than ``innerConeAngle`` and less than or equal to
``PI / 2.0``. Defaults to ``PI / 4.0``.
name : str, optional
Name of the light.
"""
def __init__(self,
color=None,
intensity=None,
range=None,
innerConeAngle=0.0,
outerConeAngle=(np.pi / 4.0),
name=None):
super(SpotLight, self).__init__(
name=name,
color=color,
intensity=intensity,
)
self.outerConeAngle = outerConeAngle
self.innerConeAngle = innerConeAngle
self.range = range
@property
def innerConeAngle(self):
"""float : The inner cone angle in radians.
"""
return self._innerConeAngle
@innerConeAngle.setter
def innerConeAngle(self, value):
if value < 0.0 or value > self.outerConeAngle:
raise ValueError('Invalid value for inner cone angle')
self._innerConeAngle = float(value)
@property
def outerConeAngle(self):
"""float : The outer cone angle in radians.
"""
return self._outerConeAngle
@outerConeAngle.setter
def outerConeAngle(self, value):
if value < 0.0 or value > np.pi / 2.0 + 1e-9:
raise ValueError('Invalid value for outer cone angle')
self._outerConeAngle = float(value)
@property
def range(self):
"""float : The cutoff distance for the light.
"""
return self._range
@range.setter
def range(self, value):
if value is not None:
value = float(value)
if value <= 0:
raise ValueError('Range must be > 0')
self._range = value
self._range = value
def _generate_shadow_texture(self, size=None):
"""Generate a shadow texture for this light.
Parameters
----------
size : int, optional
Size of texture map. Must be a positive power of two.
"""
if size is None:
size = SHADOW_TEX_SZ
self.shadow_texture = Texture(width=size, height=size,
source_channels='D', data_format=GL_FLOAT)
def _get_shadow_camera(self, scene_scale):
"""Generate and return a shadow mapping camera for this light.
Parameters
----------
scene_scale : float
Length of scene's bounding box diagonal.
Returns
-------
camera : :class:`.Camera`
The camera used to render shadowmaps for this light.
"""
return PerspectiveCamera(
znear=0.01 * scene_scale,
zfar=10 * scene_scale,
yfov=np.clip(2 * self.outerConeAngle + np.pi / 16.0, 0.0, np.pi),
aspectRatio=1.0
)
__all__ = ['Light', 'DirectionalLight', 'SpotLight', 'PointLight']
|