diff --git a/input/hdr.json b/input/hdr.json index a0ee38af..63f17fe1 100644 --- a/input/hdr.json +++ b/input/hdr.json @@ -93,13 +93,15 @@ ] } ], - "color": { - "r": 1.0, - "g": 0.0, - "b": 1.0 + "material": { + "type": "metal", + "color": { + "r": 1.0, + "g": 0.0, + "b": 1.0 + }, + "roughness": 0.05 }, - "bsdf": "metal", - "roughness": 0.05, "radius": 0.1 }, { @@ -116,13 +118,15 @@ ] } ], - "color": { - "r": 0.8, - "g": 0.8, - "b": 0.8 + "material": { + "type": "metal", + "color": { + "r": 0.8, + "g": 0.8, + "b": 0.8 + }, + "roughness": 0.00 }, - "bsdf": "metal", - "roughness": 0.00, "radius": 0.05 }, { @@ -138,14 +142,16 @@ } ] } - ], - "color": { - "r": 0.0, - "g": 0.8, - "b": 0.0 + ], + "material": { + "type": "metal", + "color": { + "r": 0.0, + "g": 0.8, + "b": 0.0 + }, + "roughness": 0.00 }, - "bsdf": "metal", - "roughness": 0.00, "radius": 0.05 }, { @@ -161,15 +167,17 @@ } ] } - ], - "color": { - "r": 1.0, - "g": 1.0, - "b": 1.0 + ], + "material": { + "type": "glass", + "color": { + "r": 1.0, + "g": 1.0, + "b": 1.0 + }, + "IOR": 1.9, + "roughness": 0 }, - "bsdf": "glass", - "IOR": 1.9, - "roughness": 0, "radius": 0.05 }, { @@ -185,15 +193,17 @@ } ] } - ], - "color": { - "r": 1.0, - "g": 0.1, - "b": 0.1 + ], + "material": { + "type": "glass", + "color": { + "r": 1.0, + "g": 0.1, + "b": 0.1 + }, + "IOR": 1.9, + "roughness": 0 }, - "bsdf": "glass", - "IOR": 1.9, - "roughness": 0, "radius": 0.025 }, { @@ -209,15 +219,17 @@ } ] } - ], - "color": { - "r": 0.1, - "g": 1.0, - "b": 0.1 + ], + "material": { + "type": "glass", + "color": { + "r": 0.1, + "g": 1.0, + "b": 0.1 + }, + "IOR": 1.9, + "roughness": 0 }, - "bsdf": "glass", - "IOR": 1.9, - "roughness": 0, "radius": 0.025 }, { @@ -234,14 +246,16 @@ ] } ], - "color": { - "r": 0.1, - "g": 0.1, - "b": 1.0 + "material": { + "type": "glass", + "color": { + "r": 0.1, + "g": 0.1, + "b": 1.0 + }, + "IOR": 1.9, + "roughness": 0 }, - "bsdf": "glass", - "IOR": 1.9, - "roughness": 0, "radius": 0.025 }, { @@ -258,14 +272,16 @@ ] } ], - "radius": 0.01, - "color": { - "r": 1.0, - "g": 0.0, - "b": 0.0 + "material": { + "type": "metal", + "color": { + "r": 1.0, + "g": 0.0, + "b": 0.0 + }, + "roughness": 1.0 }, - "bsdf": "metal", - "roughness": 1.0 + "radius": 0.01 }, { "type": "sphere", @@ -322,14 +338,16 @@ ] } ], - "radius": 2.5, - "color": { - "r": 0.0, - "g": 1.0, - "b": 0.0 + "material": { + "type": "emissive", + "color": { + "r": 0.0, + "g": 1.0, + "b": 0.0 + }, + "intensity": 10.0 }, - "bsdf": "emissive", - "intensity": 10.0 + "radius": 2.5 }, { "type": "sphere", @@ -345,14 +363,16 @@ ] } ], - "radius": 2.5, - "color": { - "r": 0.0, - "g": 0.0, - "b": 1.0 + "material": { + "type": "emissive", + "color": { + "r": 0.0, + "g": 0.0, + "b": 1.0 + }, + "intensity": 10.0 }, - "bsdf": "emissive", - "intensity": 10.0 + "radius": 2.5 }, { "type": "sphere", @@ -367,14 +387,16 @@ } ] } - ], - "color": { - "r": 1.0, - "g": 1.0, - "b": 1.0 + ], + "material": { + "type": "emissive", + "color": { + "r": 1.0, + "g": 1.0, + "b": 1.0 + }, + "intensity": 10.0 }, - "bsdf": "emissive", - "intensity": 10.0, "radius": 10 } ], diff --git a/src/datatypes/hitrecord.h b/src/datatypes/hitrecord.h index 8aa321ae..d22d0114 100644 --- a/src/datatypes/hitrecord.h +++ b/src/datatypes/hitrecord.h @@ -15,10 +15,8 @@ struct hitRecord { struct vector incident_dir; //Incident ray direction struct vector hitPoint; //Hit point vector in world space struct vector surfaceNormal; //Surface normal at that point of intersection - //FIXME: Do we even have cases without UV anymore? struct coord uv; //UV barycentric coordinates for intersection point const struct bsdfNode *bsdf; //Surface properties of the intersected object - const struct color *emission; //FIXME: Hack - Shouldn't have this here struct poly *polygon; //ptr to polygon that was encountered float distance; //Distance to intersection point int instIndex; //Instance index, negative if no intersection diff --git a/src/datatypes/material.c b/src/datatypes/material.c index 978e6343..f9e6a8e9 100644 --- a/src/datatypes/material.c +++ b/src/datatypes/material.c @@ -14,22 +14,9 @@ #include "image/texture.h" #include "../datatypes/scene.h" -static struct material emptyMaterial() { - return (struct material){0}; -} - -struct material defaultMaterial() { - struct material newMat = emptyMaterial(); - newMat.diffuse = g_gray_color; - newMat.reflectivity = 1.0f; - newMat.type = lambertian; - newMat.IOR = 1.0f; - return newMat; -} - //To showcase missing .MTL file, for example struct material warningMaterial() { - struct material newMat = emptyMaterial(); + struct material newMat = { 0 }; newMat.type = lambertian; newMat.diffuse = (struct color){1.0f, 0.0f, 0.5f, 1.0f}; return newMat; diff --git a/src/datatypes/material.h b/src/datatypes/material.h index 7a5a4d48..4089f983 100644 --- a/src/datatypes/material.h +++ b/src/datatypes/material.h @@ -64,7 +64,6 @@ struct material { struct texture *texture; struct texture *normalMap; struct texture *specularMap; - struct color ambient; struct color diffuse; struct color specular; struct color emission; @@ -79,10 +78,8 @@ struct material { float glossiness; enum bsdfType type; // FIXME: Temporary - const struct bsdfNode *bsdf; }; -struct material defaultMaterial(void); struct material warningMaterial(void); struct node_storage; diff --git a/src/datatypes/scene.c b/src/datatypes/scene.c index 3c7c50fd..3b1fa1eb 100644 --- a/src/datatypes/scene.c +++ b/src/datatypes/scene.c @@ -260,7 +260,6 @@ void destroyScene(struct world *scene) { destroyBlocks(scene->storage.node_pool); for (int i = 0; i < scene->instanceCount; ++i) { if (scene->instances[i].bsdfs) free(scene->instances[i].bsdfs); - if (scene->instances[i].emissions) free(scene->instances[i].emissions); } free(scene->instances); free(scene->meshes); diff --git a/src/datatypes/sphere.c b/src/datatypes/sphere.c index 1f2972fd..b1f84912 100644 --- a/src/datatypes/sphere.c +++ b/src/datatypes/sphere.c @@ -12,10 +12,6 @@ #include "../renderer/pathtrace.h" #include "lightray.h" -struct sphere defaultSphere() { - return (struct sphere){ 10.0f, NULL, g_black_color, 0.0f }; -} - //Calculates intersection with a sphere and a light ray bool intersect(const struct lightRay *ray, const struct sphere *sphere, float *t) { //Vector dot product of the direction diff --git a/src/datatypes/sphere.h b/src/datatypes/sphere.h index c2160e36..3030b289 100644 --- a/src/datatypes/sphere.h +++ b/src/datatypes/sphere.h @@ -11,15 +11,9 @@ #include "vector.h" #include "material.h" -//Sphere struct sphere { float radius; - const struct bsdfNode *bsdf; - struct color emission; float rayOffset; }; -struct sphere defaultSphere(void); - -//Calculates intersection between a light ray and a sphere bool rayIntersectsWithSphere(const struct lightRay *ray, const struct sphere *sphere, struct hitRecord *isect); diff --git a/src/nodes/bsdfnode.h b/src/nodes/bsdfnode.h index 999d654b..605e9f71 100644 --- a/src/nodes/bsdfnode.h +++ b/src/nodes/bsdfnode.h @@ -21,6 +21,7 @@ struct bsdfSample { struct vector out; float pdf; struct color weight; + struct color emitted; // FIXME: Not really the right place for this }; //TODO: Expand and refactor to match a standard bsdf signature with eval, sample and pdf diff --git a/src/nodes/shaders/emission.c b/src/nodes/shaders/emission.c index 3e048f9d..a70176c3 100644 --- a/src/nodes/shaders/emission.c +++ b/src/nodes/shaders/emission.c @@ -52,7 +52,7 @@ static struct bsdfSample sample(const struct bsdfNode *bsdf, sampler *sampler, c const struct vector scatterDir = vec_normalize(vec_add(record->surfaceNormal, vec_on_unit_sphere(sampler))); return (struct bsdfSample){ .out = scatterDir, - .weight = colorCoef(emitBsdf->strength->eval(emitBsdf->strength, sampler, record), emitBsdf->color->eval(emitBsdf->color, sampler, record)) + .emitted = colorCoef(emitBsdf->strength->eval(emitBsdf->strength, sampler, record), emitBsdf->color->eval(emitBsdf->color, sampler, record)) }; } diff --git a/src/renderer/instance.c b/src/renderer/instance.c index 0383c5a8..d58b4830 100644 --- a/src/renderer/instance.c +++ b/src/renderer/instance.c @@ -48,8 +48,7 @@ static bool intersectSphere(const struct instance *instance, const struct lightR if (rayIntersectsWithSphere(©, sphere, isect)) { isect->uv = getTexMapSphere(isect); isect->polygon = NULL; - isect->bsdf = sphere->bsdf; - isect->emission = &sphere->emission; + isect->bsdf = instance->bsdfs[0]; transformPoint(&isect->hitPoint, instance->composite.A); transformVectorWithTranspose(&isect->surfaceNormal, instance->composite.Ainv); return true; @@ -78,8 +77,7 @@ static bool intersectSphereVolume(const struct instance *instance, const struct isect->hitPoint = alongRay(ray, isect->distance); isect->uv = (struct coord){-1.0f, -1.0f}; isect->polygon = NULL; - isect->bsdf = volume->sphere->bsdf; - isect->emission = &volume->sphere->emission; + isect->bsdf = instance->bsdfs[0]; transformPoint(&isect->hitPoint, instance->composite.A); isect->surfaceNormal = (struct vector){1.0f, 0.0f, 0.0f}; // Will be ignored by material anyway transformVectorWithTranspose(&isect->surfaceNormal, instance->composite.Ainv); // Probably not needed @@ -165,7 +163,6 @@ static bool intersectMesh(const struct instance *instance, const struct lightRay // Repopulate uv with actual texture mapping isect->uv = getTexMapMesh(mesh, isect); isect->bsdf = instance->bsdfs[isect->polygon->materialIndex]; - isect->emission = &instance->emissions[isect->polygon->materialIndex]; transformPoint(&isect->hitPoint, instance->composite.A); transformVectorWithTranspose(&isect->surfaceNormal, instance->composite.Ainv); isect->surfaceNormal = vec_normalize(isect->surfaceNormal); @@ -193,8 +190,7 @@ static bool intersectMeshVolume(const struct instance *instance, const struct li isect->distance = record1.distance + hitDistance; isect->hitPoint = alongRay(ray, isect->distance); isect->uv = (struct coord){-1.0f, -1.0f}; - isect->bsdf = mesh->mesh->materials[0].bsdf; - isect->emission = &mesh->mesh->materials[0].emission; + isect->bsdf = instance->bsdfs[0]; transformPoint(&isect->hitPoint, instance->composite.A); isect->surfaceNormal = (struct vector){1.0f, 0.0f, 0.0f}; // Will be ignored by material anyway transformVectorWithTranspose(&isect->surfaceNormal, instance->composite.Ainv); // Probably not needed diff --git a/src/renderer/instance.h b/src/renderer/instance.h index 45ec9a1e..1ef23fa4 100644 --- a/src/renderer/instance.h +++ b/src/renderer/instance.h @@ -26,7 +26,7 @@ struct instance { struct transform composite; int bsdf_count; const struct bsdfNode **bsdfs; - struct color *emissions; + bool emits_light; bool (*intersectFn)(const struct instance *, const struct lightRay *, struct hitRecord *, sampler *); void (*getBBoxAndCenterFn)(const struct instance *, struct boundingBox *, struct vector *); void *object; diff --git a/src/renderer/pathtrace.c b/src/renderer/pathtrace.c index b06caef0..56233ef7 100644 --- a/src/renderer/pathtrace.c +++ b/src/renderer/pathtrace.c @@ -60,12 +60,14 @@ struct color path_trace(const struct lightRay *incidentRay, const struct world * break; } + const struct bsdfSample sample = isect.bsdf->sample(isect.bsdf, sampler, &isect); //TODO: emission contribution needs to be adjusted down by probability of randomly hitting it - path_radiance = colorAdd(path_radiance, colorMul(path_weight, *isect.emission)); - + //FIXME: emits_light only gets set if the root node of a shader graph is emissive, so maybe fix that + // if (true || scene->instances[isect.instIndex].emits_light) { + path_radiance = colorAdd(path_radiance, colorMul(path_weight, sample.emitted)); + // } if (bounce == max_bounces) break; - const struct bsdfSample sample = isect.bsdf->sample(isect.bsdf, sampler, &isect); currentRay = (struct lightRay){ .start = isect.hitPoint, .direction = sample.out }; const struct color attenuation = sample.weight; diff --git a/src/utils/loaders/formats/wavefront/mtlloader.c b/src/utils/loaders/formats/wavefront/mtlloader.c index 2a94fc50..aa7ddd50 100644 --- a/src/utils/loaders/formats/wavefront/mtlloader.c +++ b/src/utils/loaders/formats/wavefront/mtlloader.c @@ -71,8 +71,6 @@ struct material *parseMTLFile(const char *filePath, int *mtlCount, struct file_c return NULL; } current->name = stringCopy(peekNextToken(&line)); - } else if (stringEquals(first, "Ka")) { - current->ambient = parse_color(&line); } else if (stringEquals(first, "Kd")) { current->diffuse = parse_color(&line); } else if (stringEquals(first, "Ks")) { diff --git a/src/utils/loaders/sceneloader.c b/src/utils/loaders/sceneloader.c index ca7313d7..32d0a382 100644 --- a/src/utils/loaders/sceneloader.c +++ b/src/utils/loaders/sceneloader.c @@ -22,6 +22,7 @@ #include "../logging.h" #include "../fileio.h" #include "../string.h" +#include "vendored/cJSON.h" #ifdef WINDOWS // Sigh... #include #else @@ -38,22 +39,15 @@ #include "../../utils/timer.h" #include "../../utils/string.h" #include "../../nodes/bsdfnode.h" +#include "../../nodes/valuenode.h" +#include "../../nodes/colornode.h" +#include "../../nodes/shaders/emission.h" +#include "../../nodes/textures/constant.h" +#include "../../nodes/valuenode.h" #include "meshloader.h" struct transform parseTransformComposite(const cJSON *transforms); -static struct instance *lastInstance(struct renderer *r) { - return &r->scene->instances[r->scene->instanceCount - 1]; -} - -static struct sphere *lastSphere(struct renderer *r) { - return &r->scene->spheres[r->scene->sphereCount - 1]; -} - -static void addSphere(struct world *scene, struct sphere newSphere) { - scene->spheres[scene->sphereCount++] = newSphere; -} - static struct transform parseTransform(const cJSON *data, char *targetName) { cJSON *type = cJSON_GetObjectItem(data, "type"); if (!cJSON_IsString(type)) { @@ -739,13 +733,8 @@ void apply_materials_to_instance(struct renderer *r, struct instance *instance, if (!found) goto skip; } instance->bsdfs[i] = parseBsdfNode(r->prefs.assetPath, r->state.file_cache, &r->scene->storage, override); - //FIXME: Hack cJSON *type_string = cJSON_GetObjectItem(override, "type"); - if (type_string && stringEquals(type_string->valuestring, "emissive")) { - cJSON *color = cJSON_GetObjectItem(override, "color"); - cJSON *strength = cJSON_GetObjectItem(override, "strength"); - instance->emissions[i] = colorCoef(strength->valuedouble, parseColor(color)); - } + if (type_string && stringEquals(type_string->valuestring, "emissive")) instance->emits_light = true; ASSERT(instance->bsdfs[i]); skip: i = old_i; @@ -754,6 +743,8 @@ void apply_materials_to_instance(struct renderer *r, struct instance *instance, } else { // Single graph, map it to every material in a mesh. const struct bsdfNode *node = parseBsdfNode(r->prefs.assetPath, r->state.file_cache, &r->scene->storage, overrides); + cJSON *type_string = cJSON_GetObjectItem(override, "type"); + if (type_string && stringEquals(type_string->valuestring, "emissive")) instance->emits_light = true; for (int i = 0; i < instance->bsdf_count; ++i) { instance->bsdfs[i] = node; } @@ -796,11 +787,16 @@ static void parse_mesh_instances(struct renderer *r, const cJSON *data, struct m size_t material_count = meshes[i].materialCount; new.bsdf_count = material_count; new.bsdfs = calloc(material_count, sizeof(void *)); - new.emissions = calloc(material_count, sizeof(*new.emissions)); - // Some of these may get overridden from the json in the next step below for (size_t m = 0; m < material_count; ++m) { - new.emissions[m] = meshes[i].materials[m].emission; + if (meshes[i].materials[m].emission.red > 0.0f || + meshes[i].materials[m].emission.green > 0.0f || + meshes[i].materials[m].emission.blue > 0.0f || + meshes[i].materials[m].emission.alpha > 0.0f) { + new.emits_light = true; + // const struct node_storage *s = &r->scene->storage; + // new.bsdfs[m] = newEmission(&r->scene->storage, newConstantTexture(s, meshes[i].materials[m].emission), newConstantValue(&r->scene->storage, 1.0f)); + } } apply_materials_to_instance(r, &new, overrides, meshes[i].materials, meshes[i].materialCount); @@ -863,99 +859,55 @@ static void parseMeshes(struct renderer *r, const cJSON *data) { } static void parseSphere(struct renderer *r, const cJSON *data) { - const cJSON *color = NULL; - const cJSON *roughness = NULL; - const cJSON *IOR = NULL; + struct sphere *new = &r->scene->spheres[r->scene->sphereCount++]; + const cJSON *radius = NULL; - const cJSON *intensity = NULL; - - struct sphere newSphere = defaultSphere(); - - const cJSON *bsdf = cJSON_GetObjectItem(data, "bsdf"); - - struct material material = { 0 }; - - //TODO: Break this out to a function - if (cJSON_IsString(bsdf)) { - if (stringEquals(bsdf->valuestring, "lambertian")) { - material.type = lambertian; - } else if (stringEquals(bsdf->valuestring, "metal")) { - material.type = metal; - } else if (stringEquals(bsdf->valuestring, "glass")) { - material.type = glass; - } else if (stringEquals(bsdf->valuestring, "plastic")) { - material.type = plastic; - } else if (stringEquals(bsdf->valuestring, "emissive")) { - material.type = emission; - } - } - - color = cJSON_GetObjectItem(data, "color"); - if (color != NULL) { - switch (material.type) { - case emission: - material.emission = parseColor(color); - break; - - default: - material.ambient = parseColor(color); - material.diffuse = parseColor(color); - break; - } - } - - //FIXME: Another hack. - intensity = cJSON_GetObjectItem(data, "intensity"); - if (intensity != NULL) { - if (cJSON_IsNumber(intensity) && (material.type == emission)) { - material.emission = colorCoef(intensity->valuedouble, material.emission); - } - } - - roughness = cJSON_GetObjectItem(data, "roughness"); - if (roughness != NULL && cJSON_IsNumber(roughness)) { - material.roughness = roughness->valuedouble; - } else { - material.roughness = 0.0f; - } - - IOR = cJSON_GetObjectItem(data, "IOR"); - if (IOR != NULL && cJSON_IsNumber(IOR)) { - material.IOR = IOR->valuedouble; - } else { - material.IOR = 1.0f; - } - radius = cJSON_GetObjectItem(data, "radius"); if (radius != NULL && cJSON_IsNumber(radius)) { - newSphere.radius = radius->valuedouble; + new->radius = radius->valuedouble; } else { - newSphere.radius = 1.0f; - logr(warning, "No radius specified for sphere, setting to %.0f\n", (double)newSphere.radius); + new->radius = 1.0f; + logr(warning, "No radius specified for sphere, setting to %.0f\n", (double)new->radius); } - //FIXME: Proper materials for spheres - addSphere(r->scene, newSphere); - - const cJSON *density = cJSON_GetObjectItem(data, "density"); + // Apply this to all instances that don't have their own "materials" object + const cJSON *sphere_global_materials = cJSON_GetObjectItem(data, "material"); const cJSON *instances = cJSON_GetObjectItem(data, "instances"); const cJSON *instance = NULL; if (cJSON_IsArray(instances)) { cJSON_ArrayForEach(instance, instances) { - addInstanceToScene(r->scene, density ? new_sphere_instance(lastSphere(r), (float *)&density->valuedouble, &r->scene->storage.node_pool) : new_sphere_instance(lastSphere(r), NULL, NULL)); - lastInstance(r)->composite = parse_composite_transform(cJSON_GetObjectItem(instance, "transforms")); - } - } + const cJSON *density = cJSON_GetObjectItem(data, "density"); - const cJSON *materials = cJSON_GetObjectItem(data, "material"); - if (materials) { - lastSphere(r)->bsdf = parseBsdfNode(r->prefs.assetPath, r->state.file_cache, &r->scene->storage, materials); - } else { - lastSphere(r)->bsdf = try_to_guess_bsdf(&r->scene->storage, &material); + struct instance new_instance = { 0 }; + if (cJSON_IsNumber(density)) { + new_instance = new_sphere_instance(new, (float *)&density->valuedouble, &r->scene->storage.node_pool); + } else { + new_instance = new_sphere_instance(new, NULL, NULL); + } + + const cJSON *instance_materials = cJSON_GetObjectItem(instance, "materials"); + const cJSON *materials = instance_materials ? instance_materials : sphere_global_materials; + + new_instance.bsdf_count = 1; + new_instance.bsdfs = calloc(1, sizeof(void *)); + if (materials) { + const cJSON *material = NULL; + if (cJSON_IsArray(materials)) { + material = cJSON_GetArrayItem(materials, 0); + } else { + material = materials; + } + new_instance.bsdfs[0] = parseBsdfNode(r->prefs.assetPath, r->state.file_cache, &r->scene->storage, material); + const cJSON *type_string = cJSON_GetObjectItem(material, "type"); + if (type_string && stringEquals(type_string->valuestring, "emissive")) new_instance.emits_light = true; + } + + if (!new_instance.bsdfs[0]) new_instance.bsdfs[0] = warningBsdf(&r->scene->storage); + new_instance.composite = parse_composite_transform(cJSON_GetObjectItem(instance, "transforms")); + addInstanceToScene(r->scene, new_instance); + } } - //FIXME: Ugly, I still don't know how to express emission better than this - lastSphere(r)->emission = material.emission; } static void parsePrimitive(struct renderer *r, const cJSON *data, int idx) { @@ -977,8 +929,7 @@ static void parsePrimitives(struct renderer *r, const cJSON *data) { if (data != NULL && cJSON_IsArray(data)) { int i = 0; cJSON_ArrayForEach(primitive, data) { - parsePrimitive(r, primitive, i); - i++; + parsePrimitive(r, primitive, i++); } } }