{"id":3790,"date":"2020-09-04T05:34:59","date_gmt":"2020-09-04T05:34:59","guid":{"rendered":"https:\/\/www.migenius.com\/?p=3790"},"modified":"2020-12-16T21:47:50","modified_gmt":"2020-12-16T21:47:50","slug":"creating-lighting-programmatically-in-v8","status":"publish","type":"post","link":"https:\/\/www.migenius.com\/articles\/creating-lighting-programmatically-in-v8","title":{"rendered":"Creating Lighting Programmatically in V8"},"content":{"rendered":"\n
In this article I am going to show you how add light sources to your RealityServer scene using the V8 server-side JavaScript 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.<\/p>\n\n\n\n\n\n\n\n
This article is an updated version of this earlier post<\/a> which used the JSON-RPC API directly to create lighting. Of course that approach still works but these days using the V8 API is a much easier way to manipulate your scene. So here you will learn how to access the same functionality from within V8 using its helper classes to make it much simpler.<\/p>\n\n\n\n You’ll learn how to create the scene above purely within a V8 command, including the geometry, lighting, a section plane to cut away the wall and finally loading some pre-existing objects from disk and placing them in the scene with some different materials. As can be seen above you will have the option to turn dayligthing on or off as well.<\/p>\n\n\n\n If you haven\u2019t already done so, I highly recommend reading the article on Creating an Empty Scene with Server-side V8<\/a>. This article \u00a0will be assuming you are familiar with the information there. To follow along you will need an environment from which to edit your command as well as a running instance of RealityServer itself. I\u00a0recommend\u00a0Postman<\/a>\u00a0for sending commands, however any tool that can send HTTP POST requests will do. It can be useful to enable developer mode in the V8 configuration in your realityserver.conf<\/em> file so that your command will re-load whenever you change your script.<\/p>\n\n\n\n This article also assumes you are running RealityServer 6.0 or later and uses some features introduced in that version. Even so the information can be easily adapted to earlier versions if needed.<\/p>\n\n\n\n The command we will create for this article uses quite a few different helper classes. After creating your command file, for example tutorial_lights.js<\/em> and saving it in your V8 commands directory (by default v8\/commands<\/em>) let’s include the following helper classes.<\/p>\n\n\n This gives you a feeling for some of the topics we’ll be exploring here. There are a few that are not strictly required to get lighting working (such as the section plane) but since they were used in the original article I’ll show you how to use them.<\/p>\n\n\n\n The command definition will be very simple with only two arguments, one to give the name of the scene we want to create and another to tell it whether we want the daylighting system turned on. We need the name of the scene so we can pass it to future commands such as might be needed to render or further manipulate the scene.<\/p>\n\n\n All of the remaining code I will show is inside the body of the execute <\/em>function. I’ll progressively add to it building up the scene as we go.<\/p>\n\n\n\n Before populating it we need to create all of the basic elements for the scene. Since this was covered in the Creating and Empty Scene<\/a> article we’ll gloss over the details here. Here is the basic setup in code.<\/p>\n\n\n Something extra we’ve added here are the last two lines where we import some MDL modules which we will need later when we create our light sources and materials for the scene. The above creates an empty scene with the needed scene, options, camera, camera instance and rootgroup. This can already be rendered but will only give a black image so nothing to see yet.<\/p>\n\n\n\n We made a camera in the last section but by default it just sits at the origin pointing down the negative Z axis. Not terribly useful so we’ll get it in the right place.<\/p>\n\n\n We create a transformation matrix to rotate the camera and reposition it looking into where our scene will be. This can also be created by multiplying individual matrices as we will see later but here we just create the final matrix directly. We then set a bunch of attributes on the camera to configure the tone-mapping parameters. This will control the brightness and contrast but the parameters are like those you see in a regular camera (e.g., f-number, shutter). Finally we set a resolution and then the image aspect ratio. The tone-mapping above is setup for the scene when daylighting is turned off.<\/p>\n\n\n\n Daylighting is controlled through the environment function. You can see more details on this in the article Environment Lighting with Server-side V8<\/a>. There is a special built in environment function for daylighting called sun_and_sky <\/em>which we will use here.<\/p>\n\n\n We create the sun_and_sky <\/em>function with the parameters we want and then set an attribute on our scenes options to use it. There is also a multiplier we can set to change the overall intensity of the environment. With the exception of the sun_direction <\/em>parameter, the above parameters give a physically correct daylight system. In this case we’ve chosen the sun direction by hand to work for this scene. You can also drive this from date and time by calling out to the RS.set_sun_position<\/a> command which computes this vector based on geographic location, date and time.<\/p>\n\n\n\n Finally we adjust the camera shutter if daylighting is turned on since the scene will be much brighter in that case. You may notice in the image at the start of this article that the artificial lighting is still just barely visible. This is because the brightness of the daylight is an order of magnitude brighter. It is therefore required to adjust the tone-mapping. There is however also a command you can call out to, RS.camera_auto_exposure<\/a> to determine the correct settings for you. This works in a similar way to a regular cameras automatic exposure.<\/p>\n\n\n\n The lighting is coming soon, I promise, but first we need something to actually light up so we will create a room with some holes in the ceiling to let the daylighting in if we turn it on. We will use some new functionality for this which allows you to create a binary mesh representation for the room geometry and ask RealityServer to build it for you. Here is the room we want to make.<\/p>\n\n\n\nBefore You Begin<\/h3>\n\n\n\n
V8 Helper Classes<\/h3>\n\n\n\n
\nconst Scene = require('Scene');\nconst Camera = require('Camera');\nconst Group = require('Group');\nconst Instance = require('Instance');\nconst Options = require('Options');\nconst Light = require('Light');\nconst Lightprofile = require('Lightprofile');\nconst Polygon_mesh = require('Polygon_mesh');\nconst Section_object = require('Section_object');\nconst Mdl_function_call = require('Mdl_function_call');\nconst Mdl_material_instance = require('Mdl_material_instance');\n<\/pre><\/div>\n\n\n
Command Definition<\/h3>\n\n\n\n
\nmodule.exports.command = {\n name: 'tutorial_lights',\n description: 'Creates a scene with multiple artificial light sources.',\n groups: ['tutorial', 'javascript'],\n arguments: {\n scene_name: {\n description: 'The name of the scene the will be created.',\n type: 'String'\n },\n daylight: {\n description: 'If true then daylighting will be turned on.',\n type: 'Boolean'\n }\n },\n execute: function({scene_name, daylight}) {\n ...\n }\n};\n<\/pre><\/div>\n\n\n
Empty Scene<\/h3>\n\n\n\n
\n\/\/ Create all of the needed database elements\nconst scene = new Scene(scene_name, true);\nconst options = new Options(`${scene_name}_opt`, true);\nconst camera = new Camera(`${scene_name}_cam`, true);\nconst camera_instance = new Instance(`${scene_name}_cam_inst`, true);\nconst rootgroup = new Group(`${scene_name}_root`, true);\n\n\/\/ Attach the camera to its instance and the instance to the rootgroup\ncamera_instance.item = camera;\nrootgroup.attach(camera_instance);\n\n\/\/ Set the needed elements onto the scene\nscene.options = options;\nscene.camera_instance = camera_instance;\nscene.root_group = rootgroup;\n\n\/\/ Import MDL modules that will be required later\nScene.import_elements('${shader}\/base.mdl');\nScene.import_elements('${shader}\/nvidia\/core_definitions.mdl');\n<\/pre><\/div>\n\n\n
Camera<\/h3>\n\n\n\n
\n\/\/ Set the camera instance transform for the view\ncamera_instance.matrix = new RS.Math.Matrix4x4([\n 1.0, 0.0, 0.0, 0.0,\n 0.0, 0.0, -1.0, 0.0,\n 0.0, 1.0, 0.0, 0.0,\n -5.0, -1.5, -9.55, 1.0\n]);\n\n\/\/ Select the photographic tonemapping operator\ncamera.attributes.set('tm_tonemapper', 'mia_exposure_photographic', 'String');\ncamera.attributes.set('tm_enable_tonemapper', true, 'Boolean');\n\n\/\/ Provide all settings for the operator\ncamera.attributes.set('mip_cm2_factor', 1.0, 'Float32');\ncamera.attributes.set('mip_whitepoint', new RS.Math.Color(1,1,1), 'Color');\ncamera.attributes.set('mip_film_iso', 100.0, 'Float32');\ncamera.attributes.set('mip_camera_shutter', 10.0, 'Float32');\ncamera.attributes.set('mip_f_number', 2.0, 'Float32');\ncamera.attributes.set('mip_vignetting', 0.0, 'Float32');\ncamera.attributes.set('mip_crush_blacks', 0.2, 'Float32');\ncamera.attributes.set('mip_burn_highlights', 0.25, 'Float32');\ncamera.attributes.set('mip_burn_highlights_per_component', true, 'Boolean');\ncamera.attributes.set('mip_burn_highlights_max_component', false, 'Boolean');\ncamera.attributes.set('mip_saturation', 1.0, 'Float32');\ncamera.attributes.set('mip_gamma', 2.2, 'Float32');\n\n\/\/ Set some basic camera parameters for a perspective camera\ncamera.resolution_x = 1280;\ncamera.resolution_y = 400;\ncamera.aspect = camera.resolution_x \/ camera.resolution_y;\n<\/pre><\/div>\n\n\n
Daylighting<\/h3>\n\n\n\n
\n\/\/ Create the sun and sky environment with physically based parameters\nif (daylight) {\n let environment_function = Mdl_function_call.create(`${scene.name}_environment_function`,\n 'mdl::base::sun_and_sky', {\n on: true,\n multiplier: 0.10132,\n rgb_unit_conversion: { r: 1.0, g: 1.0, b: 1.0 },\n haze: 0.5,\n redblueshift: 0.0,\n saturation: 1.0,\n horizon_height: 0.001,\n horizon_blur: 0.1,\n ground_color: { r: 0.4, g: 0.4, b: 0.4 },\n night_color: { r: 0.0, g: 0.0, b: 0.0 },\n sun_direction: { x: -0.15, y: -0.65, z: 0.9 },\n sun_disk_intensity: 1.0,\n sun_disk_scale: 1.0,\n sun_glow_intensity: 1.0,\n y_is_up: false,\n flags: 0,\n physically_scaled_sun: true\n }\n );\n\n \/\/ Set the environment_function attribute on the options to actually use the environment\n options.attributes.set('environment_function', environment_function.name, 'Ref');\n\n \/\/ Set environment intensity to 1.0 as we want to control with the sun sky system\n options.attributes.set('environment_function_intensity', 1.0, 'Float32');\n\n \/\/ Adjust the previously setup tone-mapper\n camera.attributes.set('mip_camera_shutter', 750.0, 'Float32');\n}\n<\/pre><\/div>\n\n\n
The Room<\/h3>\n\n\n\n