Closed szellmann closed 2 years ago
This could be a stale state (removeParam
and parameter precedence of intensity
over (ir)radiance
is currently not handled): Did you set something else before intensity
?
I'm setting the intensity once on construction, right after anariNewLight
.
With
float intensity = 60.f;
anariSetParameter(anari.device, anari.light, "intensity", ANARI_FLOAT32, &intensity);
this is what I observe, on three different models:
Switching to
float intensity = 60.f;
anariSetParameter(anari.device, anari.light, "radiance", ANARI_FLOAT32, &intensity);
(the difference here is that I'm using "radiance", not "intensity", as parameter name) that's what I see:
Here's the bounding boxes for the three models:
teapot: (-3,0,-2)(3.434,3.15,2)
dragon: (-0.09362,-0.048923,-0.050414)(0.111271,0.09552,0.041207)
robot: (-1.25214,-0.000540296,-0.341388)(1.16252,1.83436,0.25572)
So the observation I made is that the perceived intensity depends on the scale of the model, as on the dragon the light seems brightest. From that I concluded that the default intensityQuantity
(OSPRay's default?) would just be irradiance, as that's a area-dependent quantity. But I can't say for sure, maybe my conclusion is wrong?
I have an own device, based on my own path tracing lib, that doesn't distinguish between the three modes, just assumes they're radiance, where the renderings look the same as when setting the radiance
parameter w/ ospray.
I refactored my code into a mini reproducer:
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <anari/anari.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
#include "stb_image_write.h"
#define TEAPOT 0
#define DRAGON 1
int main(int argc, char** argv) {
// might need to adjust this a bit, for absolute paths etc.:
assert(strncmp(argv[1],"teapot.bin",10)==0||strncmp(argv[1],"dragon.bin",10)==0);
int model = strncmp(argv[1],"teapot.bin",10)==0 ? TEAPOT : DRAGON;
uint32_t numVerts=0, numFaces=0;
FILE* fp = fopen(argv[1],"rb");
fread(&numVerts,1,sizeof(numVerts),fp);
float *vertex = (float *)malloc(numVerts*3*sizeof(float));
fread(vertex,1,numVerts*3*sizeof(float),fp);
fread(&numFaces,1,sizeof(numFaces),fp);
uint32_t *index = (uint32_t *)malloc(numFaces*3*sizeof(uint32_t));
fread(index,1,numFaces*3*sizeof(uint32_t),fp);
// printf("%u %u\n",numVerts,numFaces);
ANARILibrary library = anariLoadLibrary("environment");
ANARIDevice device = anariNewDevice(library,"default");
anariCommit(device,device);
// Create world w/ single surface
ANARIWorld world = anariNewWorld(device);
ANARIGeometry geom = anariNewGeometry(device,"triangle");
ANARIArray1D vertexPosition = anariNewArray1D(device,vertex,0,0,
ANARI_FLOAT32_VEC3,numVerts,0);
ANARIArray1D primitiveIndex = anariNewArray1D(device,index,0,0,
ANARI_UINT32_VEC3,numFaces,0);
anariSetParameter(device,geom,"vertex.position",ANARI_ARRAY1D,&vertexPosition);
anariSetParameter(device,geom,"primitive.index",ANARI_ARRAY1D,&primitiveIndex);
anariRelease(device,vertexPosition);
anariRelease(device,primitiveIndex);
anariCommit(device,geom);
ANARIMaterial mat = anariNewMaterial(device,"matte");
float kd[] = {.8f,.8f,.8f};
anariSetParameter(device,mat,"color",ANARI_FLOAT32_VEC3,kd);
ANARISurface surf = anariNewSurface(device);
anariSetParameter(device,surf,"geometry",ANARI_GEOMETRY,&geom);
anariSetParameter(device,surf,"material",ANARI_GEOMETRY,&mat);
anariCommit(device,surf);
ANARIArray1D surface = anariNewArray1D(device,&surf,0,0,ANARI_SURFACE,1,0);
anariSetParameter(device,world,"surface",ANARI_ARRAY1D,&surface);
// Set up the light source
ANARILight l = anariNewLight(device,"point");
ANARIArray1D light = anariNewArray1D(device,&l,0,0,ANARI_LIGHT,1,0);
anariSetParameter(device,world,"light",ANARI_ARRAY1D, &light);
// >>>>> ADJUST HERE!
float intensity = 60.f;
anariSetParameter(device,l,"intensity",ANARI_FLOAT32,&intensity);
//anariSetParameter(device,l,"radiance",ANARI_FLOAT32,&intensity);
// >>>>>
if (model == TEAPOT) {
float radius = .820481f;
float pos[] = {3.434,3.15,2};
anariSetParameter(device,l,"radius",ANARI_FLOAT32,&radius);
anariSetParameter(device,l,"position",ANARI_FLOAT32_VEC3,pos);
} else if (model == DRAGON) {
float radius = .0266905f;
float pos[] = {.111271,.09552,.041207};
anariSetParameter(device,l,"radius",ANARI_FLOAT32,&radius);
anariSetParameter(device,l,"position",ANARI_FLOAT32_VEC3,pos);
}
anariCommit(device,l);
anariCommit(device,world);
anariRelease(device,surface);
// Frame, renderer, etc.
ANARIRenderer renderer = anariNewRenderer(device,"default");
float bgcolor[] = {.3f,.3f,.3f,1.f};
anariSetParameter(device,renderer,"backgroundColor",ANARI_FLOAT32_VEC4,bgcolor);
anariCommit(device,renderer);
ANARIFrame frame = anariNewFrame(device);
anariSetParameter(device,frame,"world",ANARI_WORLD,&world);
anariCommit(device,frame);
ANARIDataType fbFormat = ANARI_UFIXED8_RGBA_SRGB;
anariSetParameter(device,frame,"color",ANARI_DATA_TYPE,&fbFormat);
anariSetParameter(device,frame,"renderer",ANARI_RENDERER,&renderer);
ANARICamera camera = anariNewCamera(device,"perspective");
float aspect = 1.f;
anariSetParameter(device,camera,"aspect",ANARI_FLOAT32,&aspect);
if (model == TEAPOT) {
float pos[] = {0.217,1.575,9.17682};
float view[] = {0,0,-9.17682};
float up[] = {0,1,0};
anariSetParameter(device,camera,"position",ANARI_FLOAT32_VEC3,pos);
anariSetParameter(device,camera,"direction",ANARI_FLOAT32_VEC3,view);
anariSetParameter(device,camera,"up",ANARI_FLOAT32_VEC3,up);
} else if (model == DRAGON) {
float pos[] = {0.0088255,0.0232985,0.293922};
float view[] = {0,0,-0.298525};
float up[] = {0,1,0};
anariSetParameter(device,camera,"position",ANARI_FLOAT32_VEC3,pos);
anariSetParameter(device,camera,"direction",ANARI_FLOAT32_VEC3,view);
anariSetParameter(device,camera,"up",ANARI_FLOAT32_VEC3,up);
}
anariCommit(device,camera);
anariSetParameter(device,frame,"camera",ANARI_CAMERA,&camera);
uint32_t imgSize[] = {512,512};
anariSetParameter(device,frame,"size",ANARI_UINT32_VEC2,imgSize);
anariCommit(device,frame);
int spp = 16;
for (int i=0; i<spp; ++i) {
anariRenderFrame(device,frame);
anariFrameReady(device,frame,ANARI_WAIT);
}
const uint32_t *fbPointer = (uint32_t *)anariMapFrame(device,frame,"color");
stbi_flip_vertically_on_write(1);
stbi_write_png("intensity.png",512,512,4,fbPointer,512*sizeof(uint32_t));
anariUnmapFrame(device,frame,"color");
printf("done\n");
// Omitting cleanup for brevity...
}
teapot.bin
: https://drive.google.com/file/d/1YqohbsWLCplvqx2SHHMilTKb-ndv_MKu/view?usp=sharing
dragon.bin
: https://drive.google.com/file/d/1q_C_7yNypn7RYAfzRAq4Oy8otC_oD7bZ/view?usp=sharing
and the sole non-std dependency being: https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h
I realize that the report is a bit unstructured and am sorry for that. I wonder if the issue here is that setting intensity
on an area light is just undefined behavior. Nevertheless, if one adjusts the radius
, intensity
, and radiance
parameters, the behavior I observe is that for the dragon, whose bounding box's volume is way smaller than the teapot's, the perceived intensity is much higher. And I also observe the same behavior when radius
is 0
and hence the light is a point light / virtual light source.
Maybe providing a bit more context, this is what I'm doing in my app (the repro is a bit different, but based off of that):
1.) Set up a head light: radius=0
, intensity=60
, pos=camPos
2.) Orbit around the model, with about the camPos's you can see above
What I observed was that on smaller models, the light was really bright, and on bigger models, such as the teapot, the light intensity was "about right". So seemingly, the behavior depends on the scale of the model.
3.) I incidentally also observed the same behavior when setting radius = length(bbox.max-bbox.min)*.1f;
With the intensity property only depending on solid angle (when being set as "intensity"), I'm thus surprised by this behavior. The light should, IMO, always have the same brightness in this case. The various combinations (radius 0, radius depending on bbox, intensity being set as "intensity", or as "power" or "radiance") one can test out with the repro above.
OK, I think I get the issue now. You use a "point"/sphere/area light, which exhibits a quadratic falloff and is thus not perfect for use as "headlight" (because you'd need some heuristics to scale its brightness depending on the (average) distance to the objects, or an automatic exposure / tonemapping). For a headlight I'd suggest to use a directional
light with direction=camDir
(optionally with angularDiameter
to get soft shadows).
For the "point"/sphere light the difference between intensity
and radiance
is a "constant" factor of π light_radius2. The only (?) light types where quantity power
(and thus the brightness of the light*) would depend on the size of the scene are lights in infinity (directional
and hdri
), which is quite hard to handle and thus we don't support power
for them (neither in ANARI nor in OSPRay).
Correct. That's the issue. The behavior is different than the OpenGL point lights that I'm obviously more used to (in fact, in my own lib I support constant, linear, and quadratic attenuation factors for delta lights, where the latter defaults to 0
). Incidentally, when setting intensity
and non-zero radius
on anari / OSPRay "point" lights, the resulting spherical light behaves as if it were a delta light in this regard. That's what caused the issue here. So it's my misunderstanding. Sorry for the noise - and closing the issue.
I found that when setting the
intensity
parameter on lights,anari-ospray
would set theintensityQuantity
toOSP_INTENSITY_QUANTITY_IRRADIANCE
, which would correspond to units ofW/m^2
. According to the specs, intensity is however supposed to be inW/sr
, which would correspond toOSP_INTENSITY_QUANTITY_INTENSITY
. Not sure if this is deliberate, or a bug? (With irradiance being used, the light intensity oddly depends on model size, though.)