In this article I am going to show you how add light sources to your RealityServer scene using the Web-services API. You will learn how to add several different types of lights, including a photometric light using an IES data file, an area light, a spot light and daylight. This will be a very simple example but will give you all of the pieces you need to programmatically add lighting to your scene. You can expand on the concepts shown here to make different types of lighting very easily.
If you haven’t already done so, I highly recommend reading the articles on Exploring the RealityServer JSON-RPC API and Creating a Simple Scene Programmatically. This article will be assuming you are familiar with the information there, particularly how RealityServer commands work. The scene you will build here will be created entirely by giving RealityServer commands to execute. To follow along you will need an environment from which to send these commands to RealityServer as well as a running instance of RealityServer itself. I recommend Postman for sending commands, however any tool that can send HTTP POST requests will do.
This article also assumes you are running RealityServer 4.4 build 1527.40 or later and uses some features introduced in that version. Even so the information can be easily adapted to earlier versions if needed. Of course we do recommend you use the latest version if at all possible.
He is a quick tip for when you are testing your command sequences which can be very useful if running the same sequences over and over again on your server. If you use this pattern you can keep running your command sequences without restarting your server or deleting things from the database. Here’s the code:
[ {"jsonrpc": "2.0", "method": "create_scope", "params": { "scope_name" : "exScope" }, "id": 1}, {"jsonrpc": "2.0", "method": "use_scope", "params": { "scope_name" : "exScope" }, "id": 2}, ... good stuff goes here ... {"jsonrpc": "2.0", "method": "delete_scope", "params": { "scope_name" : "exScope" }, "id": 1000} ]
If you have multiple people using your RealityServer or you plan to make multiple simultaneous requests you might want to use a more random scope name, however if you are just testing on your own server a fixed name is fine. Put all of your commands in the middle and when you run the sequence everything you make will go into the scope, when you delete the scope it will all be removed. This way you can run it over and over again without name clashes. Great for testing and experimentation.
We won’t focus on building the geometry of the scene in this article. All of the code is here for you to examine but we won’t expand too much on the detail as we want to focus on adding lights to the scene.
Before adding lighting though, we of course need something to illuminate. Using some of the concepts from earlier articles we are going to make our scene entirely using RealityServer commands rather than importing a pre-existing scene. For our lighting example we are going to build a small room with some holes in the ceiling to serve as skylights in the final steps when we add daylighting to the scene. Most of the complexity comes in the generate_mesh command and working out the input for it. I’ll leave working that out as an exercise for the reader. On the right is the geometry of the scene we are making.
Use create_scene to setup the container in our database to hold the scene information:
{"jsonrpc": "2.0", "method": "create_scene", "params": { "scene_name" : "exScene" }, "id": 1}
Call create_element with the type Options to make our rendering options:
{"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exOptions", "element_type" : "Options" }, "id": 2}
Attach the new options to the scene with the scene_set_options command:
{"jsonrpc": "2.0", "method": "scene_set_options", "params": { "scene_name" : "exScene", "options" : "exOptions" }, "id": 3}
Create a new group with the create_element command using the type Group:
{"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exRootGroup", "element_type" : "Group" }, "id": 4}
Attach the root group to the scene with the scene_set_rootgroup command:
{"jsonrpc": "2.0", "method": "scene_set_rootgroup", "params": { "scene_name" : "exScene", "group" : "exRootGroup" }, "id": 5}
Create the camera using the create_element command with the type Camera:
{"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exCamera", "element_type" : "Camera" }, "id": 6}
Set the resolution of the camera with the camera_set_resolution command:
{"jsonrpc": "2.0", "method": "camera_set_resolution", "params": { "camera_name" : "exCamera", "resolution" : { "x" : 640, "y" : 200 } }, "id": 7}
Set the aspect ratio of the camera with the camera_set_aspect command:
{"jsonrpc": "2.0", "method": "camera_set_aspect", "params": { "camera_name" : "exCamera", "aspect" : 3.2 }, "id": 8}
Create a new instance for the camera with the create_element command and the type Instance:
{"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exCameraInstance", "element_type" : "Instance" }, "id": 9}
Move and orient the camera instance into position with the instance_set_world_to_obj command:
{"jsonrpc": "2.0", "method": "instance_set_world_to_obj", "params": { "instance_name" : "exCameraInstance", "transform" : { "xx": 1.0, "xy": 0.0, "xz": 0.0, "xw": 0.0, "yx": 0.0, "yy": 0.0, "yz": -1.0, "yw": 0.0, "zx": 0.0, "zy": 1.0, "zz": 0.0, "zw": 0.0, "wx": -5.0, "wy": -1.5, "wz": -9.55, "ww": 1.0 } }, "id": 10}
Attach the actual camera to the instance we made earlier with the instance_attach command:
{"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exCameraInstance", "item_name" : "exCamera" }, "id": 11}
Attach the camera instance to the root group of the scene with the group_attach command:
{"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exCameraInstance" }, "id": 12}
Attach the camera instance to the scene definition with the scene_set_camera_instance command:
{"jsonrpc": "2.0", "method": "scene_set_camera_instance", "params": { "scene_name" : "exScene", "camera_instance" : "exCameraInstance" }, "id": 13}
Set the tone-mapping options on the camera definition with the element_set_attributes command:
{"jsonrpc": "2.0", "method": "element_set_attributes", "params": { "create" : true, "element_name" : "exCamera", "attributes" : { "tm_tonemapper" : { "type" : "String", "value" : "mia_exposure_photographic" }, "mip_cm2_factor" : { "type" : "Float32", "value" : 1.0 }, "mip_film_iso" : { "type" : "Float32", "value" : 100.0 }, "mip_camera_shutter" : { "type" : "Float32", "value" : 10.0 }, "mip_f_number" : { "type" : "Float32", "value" : 2.8 }, "mip_gamma" : { "type" : "Float32", "value" : 2.2 } } }, "id": 14}
Make the mesh object for the room using the generate_mesh command:
{"jsonrpc": "2.0", "method": "generate_mesh", "params": { "name" : "exObject", "mesh" : {"vectors":{"points":[{"x":0,"y":0,"z":0},{"x":0,"y":0,"z":3},{"x":0,"y":5,"z":0},{"x":0,"y":5,"z":3},{"x":10,"y":0,"z":0},{"x":10,"y":0,"z":3},{"x":10,"y":5,"z":0},{"x":10,"y":5,"z":3},{"x":0,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":3,"z":3},{"x":0,"y":3,"z":3},{"x":3,"y":2,"z":3},{"x":4,"y":2,"z":3},{"x":4,"y":3,"z":3},{"x":3,"y":3,"z":3},{"x":6,"y":2,"z":3},{"x":7,"y":2,"z":3},{"x":7,"y":3,"z":3},{"x":6,"y":3,"z":3},{"x":9,"y":2,"z":3},{"x":10,"y":2,"z":3},{"x":10,"y":3,"z":3},{"x":9,"y":3,"z":3}],"normals":[{"x":-1,"y":0,"z":0},{"x":0,"y":1,"z":0},{"x":1,"y":0,"z":0},{"x":0,"y":0,"z":-1},{"x":0,"y":0,"z":1},{"x":0,"y":-1,"z":0}],"uvs":[{"x":0,"y":0},{"x":0,"y":3},{"x":5,"y":0},{"x":5,"y":3},{"x":0,"y":0},{"x":0,"y":3},{"x":10,"y":0},{"x":10,"y":3},{"x":10,"y":0},{"x":0,"y":0},{"x":0,"y":5},{"x":10,"y":5},{"x":0,"y":2},{"x":0,"y":3},{"x":1,"y":2},{"x":1,"y":3},{"x":3,"y":2},{"x":3,"y":3},{"x":4,"y":2},{"x":4,"y":3},{"x":6,"y":2},{"x":6,"y":3},{"x":7,"y":2},{"x":7,"y":3},{"x":9,"y":2},{"x":9,"y":3},{"x":10,"y":2},{"x":10,"y":3}]},"vertices":[{"v":0,"n":0,"t":2},{"v":2,"n":0,"t":0},{"v":3,"n":0,"t":1},{"v":1,"n":0,"t":3},{"v":2,"n":1,"t":6},{"v":6,"n":1,"t":4},{"v":7,"n":1,"t":5},{"v":3,"n":1,"t":7},{"v":6,"n":2,"t":2},{"v":4,"n":2,"t":0},{"v":5,"n":2,"t":1},{"v":7,"n":2,"t":3},{"v":4,"n":3,"t":8},{"v":0,"n":3,"t":9},{"v":2,"n":3,"t":10},{"v":6,"n":3,"t":11},{"v":0,"n":5,"t":4},{"v":4,"n":5,"t":6},{"v":5,"n":5,"t":7},{"v":1,"n":5,"t":5},{"v":8,"n":4,"t":12},{"v":9,"n":4,"t":14},{"v":10,"n":4,"t":15},{"v":11,"n":4,"t":13},{"v":12,"n":4,"t":16},{"v":13,"n":4,"t":18},{"v":14,"n":4,"t":19},{"v":15,"n":4,"t":17},{"v":16,"n":4,"t":20},{"v":17,"n":4,"t":22},{"v":18,"n":4,"t":23},{"v":19,"n":4,"t":21},{"v":20,"n":4,"t":24},{"v":21,"n":4,"t":26},{"v":22,"n":4,"t":27},{"v":23,"n":4,"t":25},{"v":1,"n":4,"t":9},{"v":5,"n":4,"t":8},{"v":21,"n":4,"t":26},{"v":8,"n":4,"t":12},{"v":11,"n":4,"t":13},{"v":22,"n":4,"t":27},{"v":7,"n":4,"t":11},{"v":3,"n":4,"t":10}],"polygons":[[0,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]]} }, "id": 15}
Set the visible attribute on the mesh object using the element_set_attribute command:
{"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exObject", "attribute_type" : "Boolean", "attribute_name" : "visible", "attribute_value" : true, "create" : true }, "id": 16}
Create a new instance for the room mesh using the create_element command with the type Instance:
{"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exObjectInstance", "element_type" : "Instance" }, "id": 17}
Attach the mesh object to the newly created instance with the instance_attach command:
{"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exObjectInstance", "item_name" : "exObject" }, "id": 18}
Attach the instance of the room mesh to the root group with the group_attach command:
{"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exObjectInstance" }, "id": 19}
Call the import_scene_elements command to load the architectural material definition from mdl::material_examples::architectural.mdl:
{"jsonrpc": "2.0", "method": "import_scene_elements", "params": { "filename": "${shader}/material_examples/architectural.mdl" }, "id": 20}
Create an instance of the architectural material with the create_material_instance_from_definition command:
{"jsonrpc": "2.0", "method": "create_material_instance_from_definition", "params": { "arguments": { "diffuse" : { "r" : 0.7, "g" : 0.7, "b" : 0.7 }, "reflectivity" : 0.0 }, "material_definition_name": "mdl::material_examples::architectural::architectural", "material_name": "exMaterial" }, "id": 21}
Call the instance_set_material command the assign the material we just made to the instance of the room mesh:
{"jsonrpc": "2.0", "method": "instance_set_material", "params": { "instance_name" : "exObjectInstance", "material_name" : "exMaterial" }, "id": 22}
Add a section plane to cut the front of the room away so we can see inside with the element_set_attribute command:
{"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exOptions", "attribute_name" : "section_planes", "attribute_type" : "Section_plane[]", "attribute_value": [ { "origin": { "x": 0.0, "y": 0.0001, "z": 0.0 }, "normal": { "x": 0.0, "y": -1.000, "z": 0.0 }, "clip_light": true, "disabled": false } ], "create": true }, "id": 23}
This will give us a nice scene to test our lighting. Of course if you render it now, it will just come out black since there is nothing to light up the geometry we have made. So let’s move on to adding some light. We’ll add four types of lighting in this article, starting with a simple photometric light using a light profile.
Light profiles in Iray are a special type of data which is loaded from an IES file (usually with the extension .ies) which describes, for each outgoing direction of a light source, how much light is emitted. The IES data is typically provided by the lighting manufacturer however you can also find various tools for making these files yourself. So let’s add one of these nice lights to the left side of our room. Prior to doing this I have obtained an IES file called downlight.ies and placed it in a new folder on my RealityServer called ies in the content_root folder. With that file in place we can do the following:
{"jsonrpc": "2.0", "method": "import_scene_elements", "params": { "filename": "${shader}/material_examples/lights_photometric.mdl" }, "id": 25}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exIesLight", "element_type" : "Light" }, "id": 28}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exIesLightProfile", "element_type" : "Lightprofile" }, "id": 29}, {"jsonrpc": "2.0", "method": "lightprofile_set_filename", "params": { "file_name" : "ies/downlight.ies", "lightprofile_name" : "exIesLightProfile" }, "id": 30}, {"jsonrpc": "2.0", "method": "create_material_instance_from_definition", "params": { "material_definition_name" : "mdl::material_examples::lights_photometric::ies_light", "material_name" : "exIesLightShader", "arguments" : { "flux" : 1200, "profile" : "exIesLightProfile" } }, "id": 31}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exIesLight", "attribute_type" : "Ref", "attribute_name" : "material", "attribute_value" : "exIesLightShader", "create" : true }, "id": 32}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exIesLightInstance", "element_type" : "Instance" }, "id": 33}, {"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exIesLightInstance", "item_name" : "exIesLight" }, "id": 34}, {"jsonrpc": "2.0", "method": "instance_set_world_to_obj", "params": { "instance_name" : "exIesLightInstance", "transform" : { "xx": 1.0, "xy": 0.0, "xz": 0.0, "xw": 0.0, "yx": 0.0, "yy": 1.0, "yz": 0.0, "yw": 0.0, "zx": 0.0, "zy": 0.0, "zz": 1.0, "zw": 0.0, "wx": -2.0, "wy": -4.75, "wz": -2.95, "ww": 1.0 } }, "id": 35}, {"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exIesLightInstance" }, "id": 36}
First we load an MDL material (lights are actually just materials with emissive properties) from disk using the import_scene_elements command. The light material definition we will use accepts a light profile as one of its parameters. You can define your own MDL file to handle the light profile differently however that is beyond the scope of this article.
Next we use create_element with the type Light to make a light. We also need to call it again with the type Lightprofile to make our light profile. Note that this light is just the light definition, it won’t actually get used until we make an instance of it (or multiple instances if we want many of the same light in the scene) and attach it to our scenes root group. Right now the light and light profile have nothing setup so we’ll do that next.
Using the lightprofile_set_filename we can tell RealityServer to load the light profile data from the IES file into memory so the renderer can use it. We have our file in its own directory but of course you can put it anywhere you like.
We now need to make an instance (a material instance, not an element instance) of the ies_light material definition we loaded earlier using create_material_instance_from_definition so that we can attach it to the light with the element_set_attribute command. For this we set the material attribute of the light we created earlier. When we call create_material_instance_from_definition we also provide the parameters for the light material, in our case we have given it a flux (brightness) and set the profile attribute to point at the light profile element we created earlier.
The light is now all setup but not actually in the scene, for that we need to make an element instance using create_element with the type Instance. We then use instance_attach to connect the light definition (which in turn references the light material which references the light profile) to the instance of the light.
Right now the light is just sitting at the origin pointing down. We want to move it to the ceiling and the left of the room. We use the instance_set_world_to_obj command for this, providing a transformation matrix that positions the light accordingly.
Finally we attach the light instance to the root group of the scene so that it will actually render. If we now render the scene we should get something that looks like this (for the commands needed to render the scene look at the end of the article).
Notice the complex scalloping effect when the light hits the wall. This is all generated from the information in the light profile. Next time you see a small downlight in real life near a wall take a look and you are very likely to see a similar distribution of light.
Another common type of light you might want to add is an area light. These are more realistic than lights with no area (point lights) since in the real-world point lights do not exist. Area lights result in soft shadows as well which usually look much nicer than the hard shadows from point lights. We will now put a rectangular area light into the middle of the scene to see what extra properties need to be set for this.
{"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exRectLight", "element_type" : "Light" }, "id": 401}, {"jsonrpc": "2.0", "method": "light_set_area_shape", "params": { "light_name" : "exRectLight", "area_shape" : "rectangle" }, "id": 402}, {"jsonrpc": "2.0", "method": "light_set_area_size", "params": { "light_name" : "exRectLight", "area_size" : {"x": 1.2, "y" : 0.01} }, "id": 403}, {"jsonrpc": "2.0", "method": "create_material_instance_from_definition", "params": { "material_definition_name" : "mdl::material_examples::lights_photometric::diffuse_area_light", "material_name" : "exRectLightShader", "arguments" : { "flux" : 900 } }, "id": 404}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exRectLight", "attribute_type" : "Ref", "attribute_name" : "material", "attribute_value" : "exRectLightShader", "create" : true }, "id": 405}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exRectLight", "attribute_type" : "Boolean", "attribute_name" : "visible", "attribute_value" : true, "create" : true }, "id": 410}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exRectLightInstance", "element_type" : "Instance" }, "id": 406}, {"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exRectLightInstance", "item_name" : "exRectLight" }, "id": 407}, {"jsonrpc": "2.0", "method": "instance_set_world_to_obj", "params": { "instance_name" : "exRectLightInstance", "transform" : { "xx": 1.0, "xy": 0.0, "xz": 0.0, "xw": 0.0, "yx": 0.0, "yy": 1.0, "yz": 0.0, "yw": 0.0, "zx": 0.0, "zy": 0.0, "zz": 1.0, "zw": 0.0, "wx": -5.0, "wy": -4.75, "wz": -2.95, "ww": 1.0 } }, "id": 408}, {"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exRectLightInstance" }, "id": 409}
For our rectangular area light most of the steps are the same, almost identical in fact, except we leave out the parts having to do with the light profile creation and instead of creating in instance of the ies_light material we will use the diffuse_area_light material and just give it a flux. We also put it in a different position, in the middle of the back wall.
We omit the import_scene_elements command to load the MDL here since we already did that for the photometric light and the light material definition is in the same file. If you are not adding the previous light, be sure to add the appropriate import_scene_elements command to the above sequence.
Note that with this MDL light type the flux is per unit area. So making the light larger will increase the amount of light in the scene. Often you might want to specify a total output independent of the size of the area light. You can do this in MDL using the mode parameter on the material_emission structure and setting it to intensity_power. See the MDL Specifications for more details.
We also need to tell RealityServer that we want the light to be an area light. We use two special commands for this, light_set_area_shape and light_set_area_size. The shape can be either rectangle, disc, cylinder or sphere. The area_size attribute takes a structure of two floating point numbers, what they mean varies depending on the type of shape used (note that sphere and disc have a different command, light_set_area_radius). We are using a rectangle so it’s the length and width. We set this to 1.2m and 0.01m respectively.
One other optional step we took was to set the visible attribute on the light (not the light instance) using element_set_attribute. This allows you to see the area light when viewed directly (it always shows up in reflections and through transmission regardless). Adding the above to our command sequence should give the following image:
Notice how the light is emitted from the entire area of the light rather than a single point. You can experiment with different shapes and sizes easily by changing a few commands.
For our third light we are going to use a more traditional spot light with a simple spread angle and exponent based falloff. This type of light used to be used frequently before light profiles were available and can still be useful in some cases. We will make it a disc shaped area source as well. Here’s the code:
{"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exSpotLight", "element_type" : "Light" }, "id": 501}, {"jsonrpc": "2.0", "method": "light_set_area_shape", "params": { "light_name" : "exSpotLight", "area_shape" : "disc" }, "id": 502}, {"jsonrpc": "2.0", "method": "light_set_area_radius", "params": { "light_name" : "exSpotLight", "area_radius" : 0.05 }, "id": 503}, {"jsonrpc": "2.0", "method": "create_material_instance_from_definition", "params": { "material_definition_name" : "mdl::material_examples::lights_photometric::spot_light", "material_name" : "exSpotLightShader", "arguments" : { "flux" : 1500, "spot_exponent" : 2.5, "spot_spread_angle" : 1.483530 } }, "id": 504}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exSpotLight", "attribute_type" : "Ref", "attribute_name" : "material", "attribute_value" : "exSpotLightShader", "create" : true }, "id": 505}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exSpotLight", "attribute_type" : "Boolean", "attribute_name" : "visible", "attribute_value" : true, "create" : true }, "id": 410}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exSpotLightInstance", "element_type" : "Instance" }, "id": 506}, {"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exSpotLightInstance", "item_name" : "exSpotLight" }, "id": 507}, {"jsonrpc": "2.0", "method": "instance_set_world_to_obj", "params": { "instance_name" : "exSpotLightInstance", "transform" : { "xx": 1.0, "xy": 0.0, "xz": 0.0, "xw": 0.0, "yx": 0.0, "yy": 1.0, "yz": 0.0, "yw": 0.0, "zx": 0.0, "zy": 0.0, "zz": 1.0, "zw": 0.0, "wx": -8.0, "wy": -4.75, "wz": -2.95, "ww": 1.0 } }, "id": 508}, {"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exSpotLightInstance" }, "id": 509}
Again, the general process is the same as the previous two lights. This one is positioned on the right side of the rear wall though. Instead of ies_light or diffuse_area_light we will use the spot_light MDL material. This one takes some extra parameters, spot_exponent and spot_spread_angle.
The spot exponent controls how soft the edge of the spot area is, higher numbers will give a more blended edge, it should however always be greater than 0. The spread angle is given in radians and specifies the maximum angle after which no light will be emitted. So a value of 1.483530 will give a spot light with a beam angle of 85 degrees.
We also use a new command here, light_set_area_radius instead of the previous light_set_area_size command. This just takes a single value and is used for disc and sphere type area lights. Putting it all together and rendering it we should get the following image:
You might be wondering why the disc of the spotlight looks dark. This is due to how Iray treats the direct visibility of area light sources. Basically it shows you whatever light is emitted at the angle you are looking at it from. Since we are looking at it very side on and the spot light doesn’t emit any light in our direction the surface of the area source comes out dark. If you change the view to be from below looking directly up at the light it will look bright. If you do not care about accuracy you can compensate for this in the MDL material used by adding a constant illumination term so there is light coming out at all angles.
So the room we made was created with 3 holes in the ceiling for a good reason, we want somewhere for some daylight to enter. Daylight is created using an environment function rather than a light element since the light from daylight comes from essentially everywhere. Adding daylight to our scene is actually relatively simple. Here is the code:
{"jsonrpc": "2.0", "method": "import_scene_elements", "params": { "filename": "${shader}/base.mdl" }, "id": 601}, {"jsonrpc": "2.0", "method": "create_function_call_from_definition", "params": { "arguments" : { "multiplier" : 0.10132, "rgb_unit_conversion" : { "r" : 1.0, "g" : 1.0, "b" : 1.0 }, "sun_disk_intensity" : 1.0, "physically_scaled_sun" : true, "y_is_up" : false, "sun_direction" : { "x" : 0.0, "y" : 0.5, "z" : 1.0 }, "on" : true }, "function_definition_name" : "mdl::base::sun_and_sky(bool,float,color,float,float,float,float,float,color,color,float3,float,float,float,bool,int,bool)", "function_name" : "exSunSky" }, "id": 602}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exOptions", "attribute_type" : "Ref", "attribute_name" : "environment_function", "attribute_value" : "exSunSky", "create" : true }, "id": 603}, {"jsonrpc": "2.0", "method": "element_set_attributes", "params": { "create" : true, "element_name" : "exCamera", "attributes" : { "tm_tonemapper" : { "type" : "String", "value" : "mia_exposure_photographic" }, "mip_cm2_factor" : { "type" : "Float32", "value" : 1.0 }, "mip_film_iso" : { "type" : "Float32", "value" : 100.0 }, "mip_camera_shutter" : { "type" : "Float32", "value" : 250.0 }, "mip_f_number" : { "type" : "Float32", "value" : 4.0 }, "mip_gamma" : { "type" : "Float32", "value" : 2.2 } } }, "id": 603}
Iray has a built in, highly accurate and efficient daylighting model. We load this by loading base.mdl. Note that there is no base.mdl on disk, this just triggers the internal loading. With this loaded we can access the sun_and_sky MDL function.
Since we want to make an MDL function call and not a material this time we call create_function_call_from_definition. We pass in the parameters for the daylight system, the ones shown in the code above are those that give you physically based lighting if your scene is in meters.
For the sun_direction we just set it to something quite high in the sky. In reality you would usually drive this from something which computes the correct value based on location, date and time. If you have the RealityServer extras plugins installed you can use the set_sun_position command for that.
Once we have our function created we just need to use element_set_attribute to set the environment_function attribute on the options. Finally above we are resetting our tone-mapping values to be more appropriate for a scene lit by daylighting (if you skip this you may just get a white image). If all went well you should get an image like this with all of our lights and daylighting:
The bright patches on the floor are where the direct sunlight is coming into the space. The daylight system includes both the sun and the sky. The bright patches on the floor generate a lot of indirect illumination.
Now, in the last image you might have noticed we can now barely see the lights (if you look closely they do still have an effect). This is normal and correct however it bares some explanation as it is a very common question we get not just for RealityServer but also for users of our other products that use Iray like Bloom Unit.
The important thing to keep in mind is that the tone-mapper we are using is simulating a camera, not the human eye. The tone-mapper is what compresses the range of values that we compute for the lighting into something that can be displayed on the conventional monitor (which has a much lower dynamic range than real-world scenes).
It is possible to simulate the eye instead by using alternative tone-mapping techniques, such as Greg Ward’s Visibility Matching Tone Reproduction Operator for High Dynamic Range Scenes. In fact if you turn off tone-mapping in RealityServer and output a .hdr file you can give it to Radiance’s pcond utility which implements this tone-mapper. RealityServer has the ability to add tone-mapping plugins should you feel adventurous and want to implement this method.
Most of the time however this is not what you want and any technique used will involve compressing the contrast in the image for display. To understand the issue a little better let’s look at the actual lighting levels. Below the IES light on the left, with daylighting off we see an illuminance of about 115 lux. When we turn on daylighting it goes up to around 6,115 lux. This means the IES light contributes less than a 2% change in illumination relative to the light provided by the daylight.
This means there is very little contrast in this region, making the contribution from the light sources difficult to see. This can be frustrating since your eye is much better at seeing this type of variation than the camera we are simulating. However if you take out your camera phone and try to capture an image of artificial and daylighting together in a space you will see a similar result to the rendering above.
Below is the complete command set which generates the last image shown above. You can modify it to experiment with different lighting options, just remember you may need to change the tone-mapping settings when switching between something with daylighting and something without. The command set below also shows you the commands needed to do the rendering part (the last few commands).
[ {"jsonrpc": "2.0", "method": "create_scope", "params": { "scope_name" : "exScope" }, "id": 1001}, {"jsonrpc": "2.0", "method": "use_scope", "params": { "scope_name" : "exScope" }, "id": 1002}, {"jsonrpc": "2.0", "method": "create_scene", "params": { "scene_name" : "exScene" }, "id": 1003}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exOptions", "element_type" : "Options" }, "id": 1004}, {"jsonrpc": "2.0", "method": "scene_set_options", "params": { "scene_name" : "exScene", "options" : "exOptions" }, "id": 1005}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exRootGroup", "element_type" : "Group" }, "id": 1006}, {"jsonrpc": "2.0", "method": "scene_set_rootgroup", "params": { "scene_name" : "exScene", "group" : "exRootGroup" }, "id": 1007}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exCamera", "element_type" : "Camera" }, "id": 1008}, {"jsonrpc": "2.0", "method": "camera_set_resolution", "params": { "camera_name" : "exCamera", "resolution" : { "x" : 640, "y" : 200 } }, "id": 1009}, {"jsonrpc": "2.0", "method": "camera_set_aspect", "params": { "camera_name" : "exCamera", "aspect" : 3.2 }, "id": 1010}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exCameraInstance", "element_type" : "Instance" }, "id": 1011}, {"jsonrpc": "2.0", "method": "instance_set_world_to_obj", "params": { "instance_name" : "exCameraInstance", "transform" : { "xx": 1.0, "xy": 0.0, "xz": 0.0, "xw": 0.0, "yx": 0.0, "yy": 0.0, "yz": -1.0, "yw": 0.0, "zx": 0.0, "zy": 1.0, "zz": 0.0, "zw": 0.0, "wx": -5.0, "wy": -1.5, "wz": -9.55, "ww": 1.0 } }, "id": 1012}, {"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exCameraInstance", "item_name" : "exCamera" }, "id": 1013}, {"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exCameraInstance" }, "id": 1014}, {"jsonrpc": "2.0", "method": "scene_set_camera_instance", "params": { "scene_name" : "exScene", "camera_instance" : "exCameraInstance" }, "id": 1015}, {"jsonrpc": "2.0", "method": "element_set_attributes", "params": { "create" : true, "element_name" : "exCamera", "attributes" : { "tm_tonemapper" : { "type" : "String", "value" : "mia_exposure_photographic" }, "mip_cm2_factor" : { "type" : "Float32", "value" : 1.0 }, "mip_film_iso" : { "type" : "Float32", "value" : 100.0 }, "mip_camera_shutter" : { "type" : "Float32", "value" : 250.0 }, "mip_f_number" : { "type" : "Float32", "value" : 4.0 }, "mip_gamma" : { "type" : "Float32", "value" : 2.2 } } }, "id": 1016}, {"jsonrpc": "2.0", "method": "generate_mesh", "params": { "name" : "exObject", "mesh" : { "vectors" : { "points" : [ {"x":0,"y":0,"z":0}, {"x":0,"y":0,"z":3}, {"x":0,"y":5,"z":0}, {"x":0,"y":5,"z":3}, {"x":10,"y":0,"z":0}, {"x":10,"y":0,"z":3}, {"x":10,"y":5,"z":0}, {"x":10,"y":5,"z":3}, {"x":0,"y":2,"z":3}, {"x":1,"y":2,"z":3}, {"x":1,"y":3,"z":3}, {"x":0,"y":3,"z":3}, {"x":3,"y":2,"z":3}, {"x":4,"y":2,"z":3}, {"x":4,"y":3,"z":3}, {"x":3,"y":3,"z":3}, {"x":6,"y":2,"z":3}, {"x":7,"y":2,"z":3}, {"x":7,"y":3,"z":3}, {"x":6,"y":3,"z":3}, {"x":9,"y":2,"z":3}, {"x":10,"y":2,"z":3}, {"x":10,"y":3,"z":3}, {"x":9,"y":3,"z":3} ], "normals" : [ {"x":-1,"y":0,"z":0}, {"x":0,"y":1,"z":0}, {"x":1,"y":0,"z":0}, {"x":0,"y":0,"z":-1}, {"x":0,"y":0,"z":1}, {"x":0,"y":-1,"z":0} ], "uvs" : [ {"x":0,"y":0}, {"x":0,"y":3}, {"x":5,"y":0}, {"x":5,"y":3}, {"x":0,"y":0}, {"x":0,"y":3}, {"x":10,"y":0}, {"x":10,"y":3}, {"x":10,"y":0}, {"x":0,"y":0}, {"x":0,"y":5}, {"x":10,"y":5}, {"x":0,"y":2}, {"x":0,"y":3}, {"x":1,"y":2}, {"x":1,"y":3}, {"x":3,"y":2}, {"x":3,"y":3}, {"x":4,"y":2}, {"x":4,"y":3}, {"x":6,"y":2}, {"x":6,"y":3}, {"x":7,"y":2}, {"x":7,"y":3}, {"x":9,"y":2}, {"x":9,"y":3}, {"x":10,"y":2}, {"x":10,"y":3} ] }, "vertices" : [ {"v":0,"n":0,"t":2}, {"v":2,"n":0,"t":0}, {"v":3,"n":0,"t":1}, {"v":1,"n":0,"t":3}, {"v":2,"n":1,"t":6}, {"v":6,"n":1,"t":4}, {"v":7,"n":1,"t":5}, {"v":3,"n":1,"t":7}, {"v":6,"n":2,"t":2}, {"v":4,"n":2,"t":0}, {"v":5,"n":2,"t":1}, {"v":7,"n":2,"t":3}, {"v":4,"n":3,"t":8}, {"v":0,"n":3,"t":9}, {"v":2,"n":3,"t":10}, {"v":6,"n":3,"t":11}, {"v":0,"n":5,"t":4}, {"v":4,"n":5,"t":6}, {"v":5,"n":5,"t":7}, {"v":1,"n":5,"t":5}, {"v":8,"n":4,"t":12}, {"v":9,"n":4,"t":14}, {"v":10,"n":4,"t":15}, {"v":11,"n":4,"t":13}, {"v":12,"n":4,"t":16}, {"v":13,"n":4,"t":18}, {"v":14,"n":4,"t":19}, {"v":15,"n":4,"t":17}, {"v":16,"n":4,"t":20}, {"v":17,"n":4,"t":22}, {"v":18,"n":4,"t":23}, {"v":19,"n":4,"t":21}, {"v":20,"n":4,"t":24}, {"v":21,"n":4,"t":26}, {"v":22,"n":4,"t":27}, {"v":23,"n":4,"t":25}, {"v":1,"n":4,"t":9}, {"v":5,"n":4,"t":8}, {"v":21,"n":4,"t":26}, {"v":8,"n":4,"t":12}, {"v":11,"n":4,"t":13}, {"v":22,"n":4,"t":27}, {"v":7,"n":4,"t":11}, {"v":3,"n":4,"t":10} ], "polygons" : [ [0,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] ] } }, "id": 1017}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exObject", "attribute_type" : "Boolean", "attribute_name" : "visible", "attribute_value" : true, "create" : true }, "id": 1018}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exObjectInstance", "element_type" : "Instance" }, "id": 1019}, {"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exObjectInstance", "item_name" : "exObject" }, "id": 1020}, {"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exObjectInstance" }, "id": 1021}, {"jsonrpc": "2.0", "method": "import_scene_elements", "params": { "filename": "${shader}/material_examples/architectural.mdl" }, "id": 1022}, {"jsonrpc": "2.0", "method": "create_material_instance_from_definition", "params": { "arguments": { "diffuse" : { "r" : 0.7, "g" : 0.7, "b" : 0.7 }, "reflectivity" : 0.0 }, "material_definition_name": "mdl::material_examples::architectural::architectural", "material_name": "exMaterial" }, "id": 1023}, {"jsonrpc": "2.0", "method": "instance_set_material", "params": { "instance_name" : "exObjectInstance", "material_name" : "exMaterial" }, "id": 1024}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exOptions", "attribute_name" : "section_planes", "attribute_type" : "Section_plane[]", "attribute_value": [ { "origin": { "x": 0.0, "y": 0.0001, "z": 0.0 }, "normal": { "x": 0.0, "y": -1.000, "z": 0.0 }, "clip_light": true, "disabled": false } ], "create": true }, "id": 1025}, {"jsonrpc": "2.0", "method": "import_scene_elements", "params": { "filename": "${shader}/material_examples/lights_photometric.mdl" }, "id": 2001}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exIesLight", "element_type" : "Light" }, "id": 2002}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exIesLightProfile", "element_type" : "Lightprofile" }, "id": 2003}, {"jsonrpc": "2.0", "method": "lightprofile_set_filename", "params": { "file_name" : "ies/downlight.ies", "lightprofile_name" : "exIesLightProfile" }, "id": 2004}, {"jsonrpc": "2.0", "method": "create_material_instance_from_definition", "params": { "material_definition_name" : "mdl::material_examples::lights_photometric::ies_light", "material_name" : "exIesLightShader", "arguments" : { "flux" : 1200, "profile" : "exIesLightProfile" } }, "id": 2005}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exIesLight", "attribute_type" : "Ref", "attribute_name" : "material", "attribute_value" : "exIesLightShader", "create" : true }, "id": 2006}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exIesLightInstance", "element_type" : "Instance" }, "id": 2007}, {"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exIesLightInstance", "item_name" : "exIesLight" }, "id": 2008}, {"jsonrpc": "2.0", "method": "instance_set_world_to_obj", "params": { "instance_name" : "exIesLightInstance", "transform" : { "xx": 1.0, "xy": 0.0, "xz": 0.0, "xw": 0.0, "yx": 0.0, "yy": 1.0, "yz": 0.0, "yw": 0.0, "zx": 0.0, "zy": 0.0, "zz": 1.0, "zw": 0.0, "wx": -2.0, "wy": -4.75, "wz": -2.95, "ww": 1.0 } }, "id": 2009}, {"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exIesLightInstance" }, "id": 2010}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exRectLight", "element_type" : "Light" }, "id": 3001}, {"jsonrpc": "2.0", "method": "light_set_area_shape", "params": { "light_name" : "exRectLight", "area_shape" : "rectangle" }, "id": 402}, {"jsonrpc": "2.0", "method": "light_set_area_size", "params": { "light_name" : "exRectLight", "area_size" : {"x": 1.2, "y" : 0.01} }, "id": 403}, {"jsonrpc": "2.0", "method": "create_material_instance_from_definition", "params": { "material_definition_name" : "mdl::material_examples::lights_photometric::diffuse_area_light", "material_name" : "exRectLightShader", "arguments" : { "flux" : 900 } }, "id": 404}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exRectLight", "attribute_type" : "Ref", "attribute_name" : "material", "attribute_value" : "exRectLightShader", "create" : true }, "id": 405}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exRectLight", "attribute_type" : "Boolean", "attribute_name" : "visible", "attribute_value" : true, "create" : true }, "id": 410}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exRectLightInstance", "element_type" : "Instance" }, "id": 406}, {"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exRectLightInstance", "item_name" : "exRectLight" }, "id": 407}, {"jsonrpc": "2.0", "method": "instance_set_world_to_obj", "params": { "instance_name" : "exRectLightInstance", "transform" : { "xx": 1.0, "xy": 0.0, "xz": 0.0, "xw": 0.0, "yx": 0.0, "yy": 1.0, "yz": 0.0, "yw": 0.0, "zx": 0.0, "zy": 0.0, "zz": 1.0, "zw": 0.0, "wx": -5.0, "wy": -4.75, "wz": -2.95, "ww": 1.0 } }, "id": 408}, {"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exRectLightInstance" }, "id": 409}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exSpotLight", "element_type" : "Light" }, "id": 501}, {"jsonrpc": "2.0", "method": "light_set_area_shape", "params": { "light_name" : "exSpotLight", "area_shape" : "disc" }, "id": 502}, {"jsonrpc": "2.0", "method": "light_set_area_radius", "params": { "light_name" : "exSpotLight", "area_radius" : 0.05 }, "id": 503}, {"jsonrpc": "2.0", "method": "create_material_instance_from_definition", "params": { "material_definition_name" : "mdl::material_examples::lights_photometric::spot_light", "material_name" : "exSpotLightShader", "arguments" : { "flux" : 1500, "spot_exponent" : 2.5, "spot_spread_angle" : 1.483530 } }, "id": 504}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exSpotLight", "attribute_type" : "Ref", "attribute_name" : "material", "attribute_value" : "exSpotLightShader", "create" : true }, "id": 505}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exSpotLight", "attribute_type" : "Boolean", "attribute_name" : "visible", "attribute_value" : true, "create" : true }, "id": 506}, {"jsonrpc": "2.0", "method": "create_element", "params": { "element_name" : "exSpotLightInstance", "element_type" : "Instance" }, "id": 507}, {"jsonrpc": "2.0", "method": "instance_attach", "params": { "instance_name" : "exSpotLightInstance", "item_name" : "exSpotLight" }, "id": 508}, {"jsonrpc": "2.0", "method": "instance_set_world_to_obj", "params": { "instance_name" : "exSpotLightInstance", "transform" : { "xx": 1.0, "xy": 0.0, "xz": 0.0, "xw": 0.0, "yx": 0.0, "yy": 1.0, "yz": 0.0, "yw": 0.0, "zx": 0.0, "zy": 0.0, "zz": 1.0, "zw": 0.0, "wx": -8.0, "wy": -4.75, "wz": -2.95, "ww": 1.0 } }, "id": 509}, {"jsonrpc": "2.0", "method": "group_attach", "params": { "group_name" : "exRootGroup", "item_name" : "exSpotLightInstance" }, "id": 510}, {"jsonrpc": "2.0", "method": "import_scene_elements", "params": { "filename": "${shader}/base.mdl" }, "id": 601}, {"jsonrpc": "2.0", "method": "create_function_call_from_definition", "params": { "arguments" : { "multiplier" : 0.10132, "rgb_unit_conversion" : { "r" : 1.0, "g" : 1.0, "b" : 1.0 }, "sun_disk_intensity" : 1.0, "physically_scaled_sun" : true, "y_is_up" : false, "sun_direction" : { "x" : 0.0, "y" : 0.5, "z" : 1.0 }, "on" : true }, "function_definition_name" : "mdl::base::sun_and_sky(bool,float,color,float,float,float,float,float,color,color,float3,float,float,float,bool,int,bool)", "function_name" : "exSunSky" }, "id": 602}, {"jsonrpc": "2.0", "method": "element_set_attribute", "params": { "element_name" : "exOptions", "attribute_type" : "Ref", "attribute_name" : "environment_function", "attribute_value" : "exSunSky", "create" : true }, "id": 603}, {"jsonrpc": "2.0", "method": "element_set_attributes", "params": { "element_name" : "exOptions", "create" : true, "attributes" : { "progressive_rendering_max_samples" : { "type" : "Sint32", "value" : 5000 }, "progressive_rendering_max_time" : { "type" : "Sint32", "value" : 1200 } } }, "id": 701}, {"jsonrpc": "2.0", "method": "render", "params": { "scene_name" : "exScene", "renderer" : "iray", "render_context_options" : { "scheduler_mode" : { "type" : "String", "value" : "batch" } } }, "id": 702}, {"jsonrpc": "2.0", "method": "delete_scope", "params": { "scope_name" : "exScope" }, "id": 999} ]
Just note that you will need an IES file to make this work. You can download our example file from here. It will need to be placed somewhere under your content_root folder, in our example we put it into an ies sub-folder.
In this article we have covered all of the steps needed to programmatically add lighting to a scene, including different types of lights and many of the different ways to set them up. With the flexibility of using MDL for defining lights you can take things a lot further than shown here so you are definitely encouraged to explore both the MDL documentation and the general RealityServer documentation for more detail. As always, contact us if you would like more information or would like to request an evaluation of RealityServer.
Paul Arden has worked in the Computer Graphics industry for over 20 years, co-founding the architectural visualisation practice Luminova out of university before moving to mental images and NVIDIA to manage the Cloud-based rendering solution, RealityServer, now managed by migenius where Paul serves as CEO.