User Tools

Site Tools


tutorials:basic-spectral-light-modeling

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorials:basic-spectral-light-modeling [2025/01/08 21:30] MHtutorials:basic-spectral-light-modeling [2025/06/04 14:08] (current) MH
Line 7: Line 7:
 {{ :tutorials:light_preferences.png?direct&400 |}} {{ :tutorials:light_preferences.png?direct&400 |}}
  
-Spectral light simulations now deal not only with the pure calculation of light distributions but also include aspects of the principal characteristics of light, i.e., light quality, quantity, and duration.+Spectral light simulations now deal not only with the pure calculation of light distributions it's intensity (or quantity), and duration, but further includes the aspect of light quality.
  
 {{ :tutorials:light7.png?direct&400 |}} {{ :tutorials:light7.png?direct&400 |}}
  
-The main factor influencing the light quality is the light's spectral composition, commonly called colour. Thus, the compositions of different intensities of different wavelengths form the final light spectrum or colour. Below are the light spectra of typical sunlight, of common HPS lamps (high-pressure sodium lamps), as used for instance as additional light sources within greenhouses, and a red LED lamp.+The main factor influencing light quality is the light's spectral composition (of which the part visible to the human eye is often simply referred referred to as colour). Thus, the compositions of different intensities of different wavelengths form the final light spectrum or colour. Below are the light spectra of typical sunlight, of common HPS lamps (high-pressure sodium lamps), as used for instance as additional light sources within greenhouses, and a red LED lamp.
  
 {{ :tutorials:light8.png?direct&500 |}}  {{ :tutorials:light8.png?direct&500 |}} 
Line 19: Line 19:
 In GroIMP, the GPUFlux model allows us to simulate spectral light between 380 and 720 nm (default values). In GroIMP, the GPUFlux model allows us to simulate spectral light between 380 and 720 nm (default values).
  
-Note: The implementation essentially allows setting individual limits for min and //maxLambda//, extending the range of the visible light spectrum to include infra-red and ultra-violet. When the wavelength deviates significantly from visible light, ray optics is no longer the most adequate tool for describing the flow of electromagnetic radiation, as other effects become significant. Therefore, from a physical point of view, the results will no longer be meaningful. One reason for this choice—the default range of 380-720 nm—is that the model uses Smith's conversion from RGB colours to full spectra. This conversion assumes that the whole spectrum is somewhat covered by the RGB colour, thus restricting the spectrum to the visual range. But if your model uses only spectral colours as inputs, then the interval could safely be extended. +Note: The implementation essentially allows setting individual limits for min and //maxLambda//, extending the range of the visible light spectrum to include infra-red and ultra-violet. When the wavelength deviates significantly from visible light, ray optics is no longer the most adequate tool for describing the flow of electromagnetic radiation, as other effects become significant. Therefore, from a physical point of view, the results will no longer be meaningful. One reason for this choice—the default range of 380-720 nm—is that the model uses Smith's conversion from RGB colours to full spectra. This conversion assumes that the whole spectrum is somewhat covered by the RGB colour, thus restricting the spectrum to the visual range. This being said, if your model uses only spectral colours as input, then the interval could safely be extended.
  
-The spectral range //[minLambda, maxLambda]// can be divided into a user-defined number of equally-sized so-called buckets—sub-channels. They can be one, taking the whole range as one channel—which, in my opinion, would not make much sense—or can be as large as the number of integer wavelengths of the range, leading to 1 nm buckets if desired. Common numbers of buckets range from 5 to 30, but this totally depends on the application. Below is an example of a 380-720 nm spectrum divided into 20 buckets, each with a 17 nm range.+The spectral range //[minLambda, maxLambda]// can be divided into a user-defined number of equally-sized so-called buckets—sub-channels. They can be one, taking the whole range as one channel—which, in our opinion, would not make much sense—or can be as large as the number of integer wavelengths of the range, leading to 1 nm buckets if desired. Common numbers of buckets range from 5 to 30, but this totally depends on the application. Below is an example of a 380-720 nm spectrum divided into 20 buckets, each with a 17 nm range.
  
 {{ :tutorials:light9.png?direct&400 |}} {{ :tutorials:light9.png?direct&400 |}}
Line 27: Line 27:
 Setting up the GPUFlux light model within GroIMP, or more accurately XL, follows the typical Java conventions of importing the required classes, and initializing and parameterizing the light model. Setting up the GPUFlux light model within GroIMP, or more accurately XL, follows the typical Java conventions of importing the required classes, and initializing and parameterizing the light model.
  
-Note: The examples require GroIPM version >=2.0 to run. With GroIMP version 2.0 some changes on the internal package structure are made. formally classes found in  de.grogra.imp3d have been moved to de.grogra.gpuflux.imp3d to match the package name (Java 11 forbid package name split). So, if you are using objects, lights or shaders from gpuflux, they should be imported as de.grogra.gpuflux.imp3d.xxx.+Note: The examples require GroIMP version >=2.0 to run. With GroIMP version 2.0 some changes on the internal package structure are made. Formally, classes found in  de.grogra.imp3d have been moved to de.grogra.gpuflux.imp3d to match the package name (Java 11 forbids package name split). So, if you are using objects, lights or shaders from gpuflux, they should be imported as de.grogra.gpuflux.imp3d.xxx.
  
 <code java> <code java>
Line 42: Line 42:
  
 const int RAYS = 10000000; //number of simulated light rays const int RAYS = 10000000; //number of simulated light rays
-const int DEPTH = 10; //maxiaml recursion/reflection depth+const int DEPTH = 10; //maximal recursion/reflection depth
 const FluxLightModel LM = new FluxLightModel(RAYS, DEPTH); const FluxLightModel LM = new FluxLightModel(RAYS, DEPTH);
  
 protected void calculateLight() { protected void calculateLight() {
-    LM.setMeasureMode(MeasureMode.FULL_SPECTRUM); // actual the default value+    LM.setMeasureMode(MeasureMode.FULL_SPECTRUM); // currently the default value
     LM.setSpectralBuckets(21); // get 20 buckets; set to N+1 to get N     LM.setSpectralBuckets(21); // get 20 buckets; set to N+1 to get N
-    LM.setSpectralDomain(380, 720); // default settings too+    LM.setSpectralDomain(380, 720); // minimum and maximum lambda, default settings 
 +...    
 } }
 </code> </code>
Line 65: Line 66:
 LM.setCutoffPower(0.01); // default: 0.001 LM.setCutoffPower(0.01); // default: 0.001
  
-// disables the simulation of sensor+// enable or disables the simulation of sensors
 LM.setEnableSensors(true); // default: false LM.setEnableSensors(true); // default: false
  
-// sets the random seed for the random number generator; us this to obtain reproducible results+// sets the random seed for the random number generator; use this to obtain reproducible results
 LM.setRandomseed(123456); LM.setRandomseed(123456);
  
 // enable dispersion // enable dispersion
-LM.setDispersion(ture); // default: false+LM.setDispersion(true); // default: false
 </code> </code>
  
  
-After the light model is configured, it can be invoked by calling the //compute()// function as follows:+After the light model has been configurated, it can be invoked by calling the //compute()// function as follows:
  
 <code java> <code java>
Line 82: Line 83:
 </code> </code>
  
-To obtain the total amount of absorbed radiation of a node x, the //getAbsorbedPowerMeasurement// function of the light model needs to be called. The returned measurement object contains the results for the specific object x. By calling the //integrate()// function, the integral, or simply sum, will be calculated. +To obtain the total amount of absorbed radiation of a node x, the //getAbsorbedPowerMeasurement()// function of the light model needs to be called. (Note: For the GPUFlux light model one needs to call //getAbsorbedPowerMeasurement()// and not //getAbsorbedPower()// as for the Twilight light model. And the return value is a //Measurement// and not a //Spectrum//The returned measurement object contains the results for the specific object x. By calling the //integrate()// function, the integral, or simply sum, will be calculated.
  
 <code java> <code java>
 +import de.grogra.gpuflux.scene.experiment.Measurement;
 +
 Measurement spectrum = LM.getAbsorbedPowerMeasurement(x); Measurement spectrum = LM.getAbsorbedPowerMeasurement(x);
 float absorbedPower = spectrum.integrate(); float absorbedPower = spectrum.integrate();
 </code> </code>
 +
 +Have in mind, the unit here is Watt. The output power of the light sources is set in Watt and the values for absorption, reflectance, transmission are in Watt too.
  
 By doing this within a rule, the light absorption can be obtained for all objects of the specified type, such as a //Box//, as in this example: By doing this within a rule, the light absorption can be obtained for all objects of the specified type, such as a //Box//, as in this example:
Line 104: Line 109:
 <code java> <code java>
     Measurement spectrum = LM.getAbsorbedPowerMeasurement(x);     Measurement spectrum = LM.getAbsorbedPowerMeasurement(x);
-    // absorbed power for the first bucket: 380 -397nm+    // absorbed power for the first bucket: 380 - 397 nm
     float ap380_397 = spectrum.data[0];     float ap380_397 = spectrum.data[0];
  
Line 119: Line 124:
  
 ==== Light sources ==== ==== Light sources ====
-After the light model is set up, the next step is to define the spectral light sources. The GPUFlux light model works with all basic light nodes, such as //PointLight//, //SpotLight//, or //DirectionalLights//, but to fully realize the potential of spectral light modelling, it is necessary to define the emitted spectrum of the light source. The emitted spectrum can be defined as power intensities per wavelength, specifying the amplitude for specific wavelengths. Using the following spectrum will result in a dark magenta colour.+After the light model has been set up, the next step is to define the spectral light sources. The GPUFlux light model works with all basic light nodes, such as //PointLight//, //SpotLight//, or //DirectionalLight//, but to fully realize the potential of spectral light modelling, it is necessary to define the emitted spectrum of the light source. The emitted spectrum can be defined as power intensities per wavelength, specifying the amplitude for specific wavelengths. Using the following spectrum will result in a dark magenta colour.
  
 <code java> <code java>
Line 129: Line 134:
  
  
-Note: The step size does not have to be equal, and values in between are linearly interpolated. The unit of the amplitudes is either given absolutely in watts or normalized between zero and one. The wavelength array is assumed to be sorted from low to high. +Note: Step sizes do not have to be equal, and values in between are linearly interpolated. The unit of the amplitude is either given absolutely in watts or normalized between zero and one. The wavelength array is assumed to be sorted from low to high. 
  
 A spectrum, given by an array of wavelengths and corresponding amplitudes, is called a spectral curve, and in computer graphics, it defines a spectral power distribution. In GroIMP, a spectral curve can be defined using the //IrregularSpectralCurve// class, which takes the wavelength array and the corresponding amplitudes as input. The //IrregularSpectralCurve// can be used as input to the //ChannelSPD// class so that it can later be used as input for the light node.  A spectrum, given by an array of wavelengths and corresponding amplitudes, is called a spectral curve, and in computer graphics, it defines a spectral power distribution. In GroIMP, a spectral curve can be defined using the //IrregularSpectralCurve// class, which takes the wavelength array and the corresponding amplitudes as input. The //IrregularSpectralCurve// can be used as input to the //ChannelSPD// class so that it can later be used as input for the light node.
  
 <code java> <code java>
 +import de.grogra.gpuflux.imp3d.spectral.SpectralCurve;
 +import de.grogra.gpuflux.imp3d.spectral.IrregularSpectralCurve;
 +
 const float[] WAVELENGTHS = {380, 410, 420, 450, 465, 480, 490, 600, 620, 630, 640, 655, 660, 670, 690, 700, 720}; const float[] WAVELENGTHS = {380, 410, 420, 450, 465, 480, 490, 600, 620, 630, 640, 655, 660, 670, 690, 700, 720};
-const AMPLITUDES = {0.05, 0.1, 0.4, 0.63, 0.25, 0.15, 0.05, 0.01, 0.1, 0.3, 0.4, 0.85, 0.75, 0.95, 0.6, 0.25, 0.1};+const float[] AMPLITUDES = {0.05, 0.1, 0.4, 0.63, 0.25, 0.15, 0.05, 0.01, 0.1, 0.3, 0.4, 0.85, 0.75, 0.95, 0.6, 0.25, 0.1};
  
-const ChannelSPD TEST_SPD = new ChannelSPD(new IrregularSpectralCurve(WAVELENGTHS, AMPLITUDES)); +const SpectralCurve TEST_SPD = new IrregularSpectralCurve(WAVELENGTHS, AMPLITUDES)); 
 </code> </code>
 +
 +**SPD** = **S**pectral **P**ower **D**istribution
 +
  
 Besides user-defined spectral curves, GroIMP provides a set of spectral curves:  Besides user-defined spectral curves, GroIMP provides a set of spectral curves:
Line 150: Line 161:
 {{ :tutorials:light_SpectralCurve.png?direct&200 |}} {{ :tutorials:light_SpectralCurve.png?direct&200 |}}
  
- +Since these spectral curve classes all implement the same //SpectralCurve// interface, they can be used in the same way and are therefore exchangeable.
-Since these spectral curve classes all implement the same //SpectralCurve// interface, they can be used in the same way and therefore exchanged. +
  
 <code java> <code java>
 +import de.grogra.gpuflux.imp3d.spectral.SpectralCurve;
 +import de.grogra.gpuflux.imp3d.spectral.IrregularSpectralCurve;
 +import de.grogra.gpuflux.imp3d.shading.ChannelSPD; 
 +
 //user defined spectral curve, applied to an IrregularSpectralCurve //user defined spectral curve, applied to an IrregularSpectralCurve
 float[] WAVELENGTHS = {380, 485, 490, 610, 615, 720}; float[] WAVELENGTHS = {380, 485, 490, 610, 615, 720};
Line 165: Line 179:
 ChannelSPD CONST_SPD = new ChannelSPD(new ConstantSpectralCurve(0.25)); ChannelSPD CONST_SPD = new ChannelSPD(new ConstantSpectralCurve(0.25));
  
-//a regular spectral curve will apply the given intensities over the specified range ([400,700])+//a regular spectral curve will apply the given intensities across the specified range ([400,700])
 ChannelSPD REG_SPD = new ChannelSPD(new  RegularSpectralCurve(new float[] {0.1, 0.9,0.2,0.1,0.4}, 400, 700)); ChannelSPD REG_SPD = new ChannelSPD(new  RegularSpectralCurve(new float[] {0.1, 0.9,0.2,0.1,0.4}, 400, 700));
  
Line 171: Line 185:
 ChannelSPD REG_SPD = new ChannelSPD(new CIENormSpectralCurve(Attributes.CIE_NORM_D55)); ChannelSPD REG_SPD = new ChannelSPD(new CIENormSpectralCurve(Attributes.CIE_NORM_D55));
  
-// a black body spectral curve with a temperature of 5000K+//a black body spectral curve with a temperature of 5000K
 ChannelSPD REG_SPD = new ChannelSPD(new BlackbodySpectralCurve(5000)); ChannelSPD REG_SPD = new ChannelSPD(new BlackbodySpectralCurve(5000));
 </code> </code>
Line 177: Line 191:
 {{ :tutorials:light11.png?direct&600 |}} {{ :tutorials:light11.png?direct&600 |}}
  
-To use the spectral curve as input for a light source, a //SpectralLight// needs to be defined. +To use the spectral curve as input for a light source, a //SpectralLight// needs to be defined. In the example below, a //SpectralLight// is defined as //PointLight// that emitts a user-defined //IrregularSpectralCurve//.
  
 <code java> <code java>
-const float[] WAVELENGTHS = {380,385,...}; +import de.grogra.gpuflux.imp3d.objects.SpectralLight; 
-const float[] AMPLITUDES = {0.000967721, 0.000980455...};+import de.grogra.gpuflux.imp3d.spectral.SpectralCurve; 
 +import de.grogra.gpuflux.imp3d.spectral.IrregularSpectralCurve; 
 +  
 +//user defined spectral curve, applied to an IrregularSpectralCurve 
 +const float[] WAVELENGTHS = {380, 485490, 610, 615, 720}; 
 +const float[] AMPLITUDES = {0,0,1,1,0,0}; 
 +const SpectralCurve TEST_SPD = new IrregularSpectralCurve(WAVELENGTHS, AMPLITUDES);  
  
 module MyLamp extends LightNode() { module MyLamp extends LightNode() {
     {     {
         setLight(         setLight(
-            new SpectralLight(new IrregularSpectralCurve(WAVELENGTHS, AMPLITUDES)).(+            new SpectralLight(TEST_SPD).(
                 setPower(100), // [W]                 setPower(100), // [W]
                 setLight(new PointLight())                 setLight(new PointLight())
Line 193: Line 214:
     }     }
 } }
 +
 +protected void init() [
 + Axiom ==> MyLamp;
 +]
 </code> </code>
  
 +Note: //ChannelSPD// are the input for shader, while //SpectralCurve// is the expected input data type for //LightNodes// objects.
 +
 +Within the //CIENormSpectralCurve// class, a small database of several predefined standard spectral curves is given:
 +
 +  * at 1nm resolution [300, 780]
 +    * A, D65
 +  * at 5nm resolution [300, 780]
 +    * A, C, D50, D55, D65, D75
 +  * at 5nm resolution [380, 780]
 +    * FL1-12, FL3_1-15, HP1-5
 +
 +For instance, to use the predefined CIE NORM D65 for typical sun light within a user defined light module, one could use the following code.
 +
 +<code java>
 +import de.grogra.gpuflux.imp3d.spectral.CIENormSpectralCurve; 
 +import de.grogra.gpuflux.imp3d.objects.SpectralLight;
 +
 +//define a light module
 +module MyLamp extends LightNode {
 +  {
 +    setLight(
 +      new SpectralLight( new CIENormSpectralCurve(Attributes.CIE_NORM_D65) ).(
 + setPower(100), //[W]
 + setLight(new PointLight())
 +      ) //end SpectralLight
 +    ); //end setLight
 +  }
 +}
 +</code>
 +
 +
 +Using the SPD, we define the light 'composition' or light quality.
 +
 +In the above example, a //PointLight// is used as light source to emit the wanted spectrum. In the next step, we will continue and show how to define and use user-defined light emission pattern. The keyword here is **PLD** = **P**hysical **L**ight **D**istribution.
  
-To complete the definition of a light source, besides the spectral power distribution, the physical light distribution (PLD), which defines the light pattern, needs to be defined. This is especially helpful or necessary for any definition of artificial light sources, such as those found in greenhouses, including HPS lamps or modern LED-based light systems.+This is especially helpful or necessary for any definition of artificial light sources, such as those found in greenhouses, including HPS lamps or modern LED-based light systems, which are usually equiped with a reflector.
  
-The physical light distribution can be defined as a polar distribution diagram (also called polar curve) showing the luminous intensity values with increasing angles from two imaginary axes of the lamp which is placed in the centre. Red: 0–180◦ plane, blue 90–270◦ plane. On the right of the Figure below, a 3D visualisation of the same light source is given. The colour of each point (gradient from black to bright red), as well as the distance to the light source, both indicate the power emitted by the light source in a particular direction per unit solid angle.+The physical light distribution can be defined as a polar distribution diagram (also called polar curve) showing the luminous intensity values with increasing angles from two imaginary axes of the lampwhich is placed in the centre. Red: 0–180◦ plane, blue 90–270◦ plane. On the right of the figure below, a 3D visualisation of the same light source is given. The colour of each point (gradient from black to bright red), as well as the distance to the light source, both indicate the power emitted by the light source in a particular direction per unit solid angle.
  
 {{ :tutorials:light_PLD.png?direct&500 |}} {{ :tutorials:light_PLD.png?direct&500 |}}
Line 218: Line 277:
 </code> </code>
  
-The result of the light ray visualization, i.e., the visualization of the physical light distribution, could look like the image below for different light sources: a) spotlight, with a defined opening angle; b) user-defined distribution; c) point light, equally distributed; d) directional light, equal distribution over an area. +The result of the light ray visualization, i.e., the visualization of the physical light distribution, could look like the image below for different light sources: a) spotlight, with a defined opening angle; b) user-defined distribution; c) point light, equally distributed; d) directional light, equal distribution across an area.
  
 {{ :tutorials:light_visulizedLightSources.png?direct&500 |}} {{ :tutorials:light_visulizedLightSources.png?direct&500 |}}
  
-To see a more realistic light pattern, the scene needs to be rendered using one of the light models. Below is a rendered image of the LampDemo.gsz, as it can be found in the GroIMP internal example gallery. +To see a more realistic light pattern, the scene needs to be rendered using one of the light models. Below is a rendered image of the //LampDemo.gsz//, as it can be found in the GroIMP internal example gallery.
  
 {{ :tutorials:light_lampDemo.png?direct&500 |}} {{ :tutorials:light_lampDemo.png?direct&500 |}}
Line 229: Line 288:
 Defining a PLD for a light source can be done in two ways: 1) 'manually' by defining the polar curve as an array of intensities within XL, or 2) by importing a PLD file—as it is provided by most professional light companies for their products. Defining a PLD for a light source can be done in two ways: 1) 'manually' by defining the polar curve as an array of intensities within XL, or 2) by importing a PLD file—as it is provided by most professional light companies for their products.
  
-In any case, instead of one of the predefined light sources, such as //PointLight//, //SpotLight//, or //DirectionalLight//, a so-called //PhysicalLight// needs to be defined. The //PhysicalLight// allows us to apply a PLD to it. For the 'manual way,' the PLD is defined as a two-dimensional array (called DISTRIBUTION in the code snippet below) that is then passed as an input parameter to the //PhysicalLight// class.+In any case, instead of one of the predefined light sources, such as //PointLight//, //SpotLight//, or //DirectionalLight//, a so-called **//PhysicalLight//** needs to be defined. The //PhysicalLight// allows us to apply a PLD to it. For the 'manual way,' the PLD is defined as a two-dimensional array (called DISTRIBUTION in the code snippet below) that is then passed as an input parameter to the //PhysicalLight// class.
  
  
Line 254: Line 313:
 } }
 </code> </code>
 +
 +
 +A PLD distribution is defined as two-dimensional array, where the values of each row represent the intensities in one direction, starting at 90 degree (above the light source) going down to 270 degree (right below the light source), covering 180 degree. The values given are equally distributed of the 180 degree, e.g., when only three values are given, the first will be associated with 90 degree, the second with zero and the third with 270 degree. The rows on the other hand are equally distributed around the virtual z-axis, around the light source. If only one row is given, this distribution defined within the first row, will be used equally around the whole light source. If two rows are given, the first one will be used at zero degree and the second one at 180 degree. The values in-between are interpolated. The intensities are given in ether in absolute values (in Watt) or in percent in the range or zero to one or zero to 100. They are normalized internally again.
  
  
Line 260: Line 322:
 After the definition of the global illumination model and the light sources, the last missing part required for proper spectral light modelling is the definition of the local illumination model. In computer graphics, the tools used are called Shaders. A shader defines the local optical properties of an object, namely the values for reflection, absorption, and transmission. The Phong illumination model, or Phong shader for short, allows us to define all required aspects. After the definition of the global illumination model and the light sources, the last missing part required for proper spectral light modelling is the definition of the local illumination model. In computer graphics, the tools used are called Shaders. A shader defines the local optical properties of an object, namely the values for reflection, absorption, and transmission. The Phong illumination model, or Phong shader for short, allows us to define all required aspects.
  
-In the same way as the spectral curves are defined for the light sources, the spectrum for reflectance and transmission needs to be defined for our spectral shader. The values for absorption are obtained as the 'remaining radiation', i.e., the difference between reflectance and transmission, when we subtract the reflectance and transmission from the total of incoming radiation: Absorption = Total - Reflectance - Transmission.+In the same way as the spectral curves are defined for the light sources, the spectrum for reflectance and transmittance needs to be defined for our spectral shader. The values for absorptance are obtained as the 'remaining radiation', i.e., the difference between reflectance and transmittance, when we subtract the reflectance and transmittance from the total incoming radiation: Absorptance = Total - Reflectance - Transmittance.
  
-Note: there is no check of plausibility implemented within the Phong shader. The user needs to make sure that the sum of reflectance and transmission is not higher than the actual incoming radiation. You cannot reflect or transmit more than what was incoming; otherwise, the object would be a light source emitting light.+Note: we employ the terms //absorptance//, //transmittance//, and //reflectance// to designate optical properties of an object. These properties will lead to measured/simulated, actual //absorption//, //transmission// and //reflection// of light, depending on the power, position, and orientation of the light source(s). 
 + 
 +Note: there is no check of plausibility implemented within the Phong shader. The user needs to make sure that the sum of reflection and transmission is not higher than the actual incoming radiation. An object cannot reflect or transmit more light than it received; otherwise, it would itself be a light source emitting light.
  
 A Phong shader can be defined as following: A Phong shader can be defined as following:
  
 <code java> <code java>
-float[] WAVELENGTHS = {380, 485, 490, 610, 615, 720}+import de.grogra.gpuflux.imp3d.shading.ChannelSPD
-float[] AMPLITUDES = {0,0,1,1,0,0}+import de.grogra.gpuflux.imp3d.spectral.IrregularSpectralCurve
-ChannelSPD GREEN_SPD = new ChannelSPD(new IrregularSpectralCurve(WAVELENGTHS, AMPLITUDES))+import de.grogra.gpuflux.imp3d.spectral.RGBSpectralCurve; 
 +import de.grogra.gpuflux.imp3d.spectral.ConstantSpectralCurve;
  
-ChannelSPD RED_SPD new ChannelSPD(new RGBSpectralCurve(0.8,0,0));  +static float[] WAVELENGTHS {380, 485, 490, 610, 615, 720}; 
-ChannelSPD CONST_SPD = new ChannelSPD(new ConstantSpectralCurve(0.25)); +static float[] AMPLITUDES = {0,0,1,1,0,0}
 +static ChannelSPD GREEN_SPD = new ChannelSPD(new IrregularSpectralCurve(WAVELENGTHS, AMPLITUDES)); 
  
-Phong myShader = new Phong(); +static ChannelSPD RED_SPD = new ChannelSPD(new RGBSpectralCurve(0.8,0,0));  
-myShader.setDiffuse(GREEN_SPD); +static ChannelSPD CONST_SPD = new ChannelSPD(new ConstantSpectralCurve(0.25)); 
-myShader.setTransparency(RED_SPD);+
  
 +//shader definition as global variable
 +Phong myShader0 = new Phong();
 +static {
 +  myShader0.setDiffuse(GREEN_SPD);
 +  myShader0.setTransparency(RED_SPD);
 +}
 +
 +//and use of the global shader within a module that is interpreted as Box
 +module TestBox ==> {
 +} Box(0.001,1,1).(setShader(myShader0));
  
-//within a module that extends a Box+ 
 +//or define the shader within a module that extends a Box
 module TestBox extends Box(0.001,1,1) { module TestBox extends Box(0.001,1,1) {
          
Line 297: Line 373:
     }     }
 } }
- 
-//as a module that is interpreted as Box 
-module TestBox ==> { 
-    Phong myShader = new Phong(); 
-} Box(0.001,1,1).(setShader(myShader)); 
 </code> </code>
  
-Within the //CIENormSpectralCurve// class, a small database of several predefined standard spectral curves is given: +Note: Do NOT mix common RGB shaders (like the //RGBAShader//) and spectral shaders! This applies to having RGB and spectral shaders within the same model but in different objects but also for one (Phong) shader that combines RGB and spectral shaders. The ranges of common RGB shaders will most probably not match the ranges of the other shader and the simulated light spectrum which will inevitably lead to false results.
- +
-  * on 1nm resolution [300, 780] +
-    * A, D65 +
-  * on 5nm resolution [300, 780] +
-    * A, C, D50, D55, D65, D75 +
-  * on 5nm resolution [380, 780] +
-    * FL1-12, FL3_1-15, HP1-5 +
- +
-For instance, to use the predefined CIE NORM D65 for typical sun light within a user defined light module, one could use the following code. +
- +
-<code java> +
-import de.grogra.imp3d.objects.Attributes; +
-import de.grogra.gpuflux.imp3d.spectral.CIENormSpectralCurve;  +
- +
-//define a light module +
-module MyLamp extends LightNode { +
-  { +
-    setLight( +
-      new SpectralLight( +
-        new CIENormSpectralCurve(Attributes.CIE_NORM_D65) +
-      ).( +
- setPower(100), //[W] +
- setLight( +
-   new SpotLight(DISTRIBUTION).( +
-     setVisualize(true), //activate light ray visualization +
-            setNumberofrays(250),  +
-            setRaylength(1)// [m] +
-          ) +
-        ) +
-      ) //end SpectralLight +
-    ); //end setLight +
-  } +
-+
-</code> +
- +
- +
- +
-Note: Do NOT mix common RGB shader like the //RGBAShader// with spectral shader! This applies for having RGB and spectral shader within the same model but in different objects but also for one (Phong) shader thas combines RGB and spectral shader. The ranges of common RGB shader will most probably not match the ranges of the other shader and simulated light spectrum what will inevitable lead to fals results.+
  
  
-==== Sensor nodes ==== 
- 
-To monitor light distributions with a scene without interfering, GroIMP provides the //SensorNode// class, a sphere that can be placed arbitrarily within the scene. To obtain the sensed spectrum, the //getSensedIrradianceMeasurement// function needs to be called. 
- 
-Note: The size of the sensor node directly correlates with the probability of got hit by a light ray. For a very small sphere the probability to got hit by a light ray is relatively low, so the number of light rays simulated by the light model needs to be much larger to get repayable results. Therefore, better not to use very small sensor nodes. 
- 
-Note: The colour of the sensor node determines which wavelengths should be observed. The default value is white, what stands for monitor all colours. If, for instance, the sensor colour is set to red, only red spectra will be sensed. 
- 
-Note: The output of a sensor node is normalized to absorbed radiance per square meter, independent of the actual size of the sensor.  
- 
-Note: Sensor nodes can be enabled and disabled for the light model using the LM.setEnableSensors(true/false) function. By default they are disabled, since GroIMP version 2.1.4, before they were enabled by default. Having them disabled speeds up the light computation time for scenarios where not sensor nodes are involved.  
- 
-<code java> 
-// create a 5cm, white sensor node 
-Axiom ==> SensorNode().(setRadius(0.05), setColor(1, 1, 1)); 
- 
-//check what the sensor node has sensed 
-x:SensorNode ::> { 
-    Measurement spectrum = lm.getSensedIrradianceMeasurement(x); 
-    float absorbedPower = spectrum.integrate(); 
-    ... 
-} 
-</code> 
  
  
Line 393: Line 403:
 </code> </code>
  
 +GroIMP supports the inport of 'Spectral Distribution Files, *.spd' as input for spectral curves, and 'Luminance Files, *.lum' as well as 'IES Light Distribution Files, *.ies' as inport formats for PLDs.
  
 ==== Example ==== ==== Example ====
Line 423: Line 433:
  println("Run GPU light model", 0x000000);  println("Run GPU light model", 0x000000);
  FluxLightModel GPU_LM = new FluxLightModel(RAYS, DEPTH);  FluxLightModel GPU_LM = new FluxLightModel(RAYS, DEPTH);
- GPU_LM.setSeed(12345); // to produce reproducible results+ GPU_LM.setSeed(1234567890); // to produce reproducible results
  GPU_LM.setMeasureMode(MeasureMode.FULL_SPECTRUM);  GPU_LM.setMeasureMode(MeasureMode.FULL_SPECTRUM);
  GPU_LM.setSpectralDomain(300,800);// spectral range monitored  GPU_LM.setSpectralDomain(300,800);// spectral range monitored
Line 431: Line 441:
 </code> </code>
  
-The model will run and directly when saved, creates an instance of the light model, set wanted parameters and run it. there will be no output (except the one form the light model itself, stating that it was executed and giving some statistics on the scene). +The model will run and directly when saved, creates an instance of the light model, set wanted parameters and run it. there will be no output (except the one form the light model itself, stating that it was executed and giving some statistics on the scene and further stating that no light sources could be found within the scene). 
  
 If you already get errors here, your system most probably does not support spectral light modelling. If you already get errors here, your system most probably does not support spectral light modelling.
Line 501: Line 511:
  println("Run GPU light model", 0x000000);  println("Run GPU light model", 0x000000);
  FluxLightModel GPU_LM = new FluxLightModel(RAYS, DEPTH);  FluxLightModel GPU_LM = new FluxLightModel(RAYS, DEPTH);
- GPU_LM.setSeed(12345); // to produce reproduceable results+ GPU_LM.setSeed(1234567890); // to produce reproduceable results
  GPU_LM.setMeasureMode(MeasureMode.FULL_SPECTRUM);  GPU_LM.setMeasureMode(MeasureMode.FULL_SPECTRUM);
  GPU_LM.setSpectralDomain(300,800);// spectral range monitored  GPU_LM.setSpectralDomain(300,800);// spectral range monitored
Line 599: Line 609:
  println("Run GPU light model", 0x000000);  println("Run GPU light model", 0x000000);
  FluxLightModel GPU_LM = new FluxLightModel(RAYS, DEPTH);  FluxLightModel GPU_LM = new FluxLightModel(RAYS, DEPTH);
- GPU_LM.setSeed(12345); // to produce reproduceable results+ GPU_LM.setSeed(1234567890); // to produce reproduceable results
  GPU_LM.setMeasureMode(MeasureMode.FULL_SPECTRUM);  GPU_LM.setMeasureMode(MeasureMode.FULL_SPECTRUM);
  GPU_LM.setSpectralDomain(300,800);// spectral range monitored  GPU_LM.setSpectralDomain(300,800);// spectral range monitored
Line 719: Line 729:
  println("Run GPU light model", 0x000000);  println("Run GPU light model", 0x000000);
  FluxLightModel GPU_LM = new FluxLightModel(RAYS, DEPTH);  FluxLightModel GPU_LM = new FluxLightModel(RAYS, DEPTH);
- GPU_LM.setSeed(12345); // to produce reproduceable results+ GPU_LM.setSeed(1234567890); // to produce reproduceable results
  GPU_LM.setMeasureMode(MeasureMode.FULL_SPECTRUM);  GPU_LM.setMeasureMode(MeasureMode.FULL_SPECTRUM);
  GPU_LM.setSpectralDomain(300,800);// spectral range monitored  GPU_LM.setSpectralDomain(300,800);// spectral range monitored
Line 730: Line 740:
  x:TestShader ::> { ms = GPU_LM.getAbsorbedPowerMeasurement(x); }  x:TestShader ::> { ms = GPU_LM.getAbsorbedPowerMeasurement(x); }
  ]  ]
- print("absorbed = "+ms);println(""+ms.integrate()+" = "+ms, 0xff0000);+ print("absorbed = "+ms);println(" "+ms.integrate(), 0xff0000);
   
  //plot the absorption spectrum  //plot the absorption spectrum
  for(int i:(0:ms.data.length-1)) {  for(int i:(0:ms.data.length-1)) {
- absorbedChart.addRow().(set(1, 300+i*16.129, ms.data[i])); //500/31=16.129+ absorbedChart.addRow().(set(1, 300+(i+0.5)*16.129, ms.data[i])); //500/31=16.129
  }  }
 } }
 </code> </code>
 +
 +
 +
 +==== Dispersion ====
 +
 +The GPUFlux light model also supports the simulation of dispersion effects – the phenomenon where light separates into its constituent colours due to variations in the reflective index with wavelengths. Different wavelengths of light refract (bend) at different angles when passing through a medium like glass, causing the colours to separate. Essentially, shorter wavelengths like violet and blue bend more than longer wavelengths like red.
 +
 +As default the simulation of dispersion is turned off - to speed up calculations - but can be enabled in the preferences of the GPUFlux light model or within the code as following:
 +
 +<code java>
 +FluxLightModel LM = new FluxLightModel(RAYS, DEPTH);
 +
 +// enable dispersion
 +LM.setDispersion(true); // default: false
 +</code>
 +
 +
 +When enabled and applied to a scene containing some ‘diamonds’ (imported OBJ objects), one can generate things like shown below (Henke and Buck-Sorlin 2018):
 +
 +{{ :tutorials:dispersion.jpg?direct&600 |}}
  
  
tutorials/basic-spectral-light-modeling.1736368242.txt.gz · Last modified: 2025/01/08 21:30 by MH