It allows to implement tricks based on a knowledge whether the path ever cam through a portal or not, and even something more advanced based on the number of portals. The main current objective is for strokes shading: stroke shader uses Ray Portal BSDF to place ray to the center of the stroke and point it in the direction of the surface it is generated for. This gives stroke a single color which matches shading of the original object. For this usecase to work the ray bounced from the original surface should ignore the strokes, which is now possible by using Portal Depth input and mixing with the Transparent BSDF. It also helps to make shading look better when there are multiple stroke layers. A solution of using portal depth is chosen over a single flag due to various factors: - Last time we've looked into it it was a bit tricky to implement as a flag due to us running out of bits. - It feels to be more flexible solution, even though it is a bit hard to come up with 100% compelling setup for it. - It needs to be slightly different from the current "Is Foo" flags, and be more "Is Portal Descendant" or something. An extra uint16 is added to the state to count the portal depth, but it is only allocated for scenes that use Ray Portal BSDF. Portal BSDF still increments Transparent bounce, as it is required to have some "limiting" factor so that ray does not get infinitely move to different place of the scene. Ref #125213 Pull Request: https://projects.blender.org/blender/blender/pulls/143107
1706 lines
55 KiB
C++
1706 lines
55 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
/* TODO(sergey): There is a bit of headers dependency hell going on
|
|
* here, so for now we just put here. In the future it might be better
|
|
* to have dedicated file for such tweaks.
|
|
*/
|
|
#if (defined(__GNUC__) && !defined(__clang__)) && defined(NDEBUG)
|
|
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
|
# pragma GCC diagnostic ignored "-Wuninitialized"
|
|
#endif
|
|
|
|
#include <cstring>
|
|
|
|
#include "scene/colorspace.h"
|
|
#include "scene/object.h"
|
|
|
|
#include "util/log.h"
|
|
#include "util/string.h"
|
|
|
|
#include "kernel/device/cpu/image.h"
|
|
|
|
#include "kernel/osl/globals.h"
|
|
#include "kernel/osl/services.h"
|
|
#include "kernel/osl/services_shared.h"
|
|
#include "kernel/osl/types.h"
|
|
|
|
#include "kernel/integrator/state.h"
|
|
|
|
#include "kernel/geom/primitive.h"
|
|
#include "kernel/geom/shader_data.h"
|
|
|
|
#include "kernel/bvh/bvh.h"
|
|
|
|
#include "kernel/camera/camera.h"
|
|
|
|
#include "kernel/svm/ao.h"
|
|
#include "kernel/svm/bevel.h"
|
|
|
|
#include "kernel/util/ies.h"
|
|
#include "kernel/util/texture_3d.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
/* RenderServices implementation */
|
|
|
|
static void copy_matrix(OSL::Matrix44 &m, const Transform &tfm)
|
|
{
|
|
ProjectionTransform t = projection_transpose(ProjectionTransform(tfm));
|
|
memcpy((float *)&m, (const float *)&t, sizeof(m));
|
|
}
|
|
|
|
static void copy_matrix(OSL::Matrix44 &m, const ProjectionTransform &tfm)
|
|
{
|
|
ProjectionTransform t = projection_transpose(tfm);
|
|
memcpy((float *)&m, (const float *)&t, sizeof(m));
|
|
}
|
|
|
|
/* static ustrings */
|
|
ustring OSLRenderServices::u_distance("distance");
|
|
ustring OSLRenderServices::u_index("index");
|
|
ustring OSLRenderServices::u_world("world");
|
|
ustring OSLRenderServices::u_camera("camera");
|
|
ustring OSLRenderServices::u_screen("screen");
|
|
ustring OSLRenderServices::u_raster("raster");
|
|
ustring OSLRenderServices::u_ndc("NDC");
|
|
ustring OSLRenderServices::u_object_location("object:location");
|
|
ustring OSLRenderServices::u_object_color("object:color");
|
|
ustring OSLRenderServices::u_object_alpha("object:alpha");
|
|
ustring OSLRenderServices::u_object_index("object:index");
|
|
ustring OSLRenderServices::u_object_is_light("object:is_light");
|
|
ustring OSLRenderServices::u_bump_map_normal("geom:bump_map_normal");
|
|
ustring OSLRenderServices::u_geom_dupli_generated("geom:dupli_generated");
|
|
ustring OSLRenderServices::u_geom_dupli_uv("geom:dupli_uv");
|
|
ustring OSLRenderServices::u_material_index("material:index");
|
|
ustring OSLRenderServices::u_object_random("object:random");
|
|
ustring OSLRenderServices::u_particle_index("particle:index");
|
|
ustring OSLRenderServices::u_particle_random("particle:random");
|
|
ustring OSLRenderServices::u_particle_age("particle:age");
|
|
ustring OSLRenderServices::u_particle_lifetime("particle:lifetime");
|
|
ustring OSLRenderServices::u_particle_location("particle:location");
|
|
ustring OSLRenderServices::u_particle_rotation("particle:rotation");
|
|
ustring OSLRenderServices::u_particle_size("particle:size");
|
|
ustring OSLRenderServices::u_particle_velocity("particle:velocity");
|
|
ustring OSLRenderServices::u_particle_angular_velocity("particle:angular_velocity");
|
|
ustring OSLRenderServices::u_geom_numpolyvertices("geom:numpolyvertices");
|
|
ustring OSLRenderServices::u_geom_trianglevertices("geom:trianglevertices");
|
|
ustring OSLRenderServices::u_geom_polyvertices("geom:polyvertices");
|
|
ustring OSLRenderServices::u_geom_name("geom:name");
|
|
ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced");
|
|
ustring OSLRenderServices::u_is_smooth("geom:is_smooth");
|
|
ustring OSLRenderServices::u_is_curve("geom:is_curve");
|
|
ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness");
|
|
ustring OSLRenderServices::u_curve_length("geom:curve_length");
|
|
ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal");
|
|
ustring OSLRenderServices::u_curve_random("geom:curve_random");
|
|
ustring OSLRenderServices::u_is_point("geom:is_point");
|
|
ustring OSLRenderServices::u_point_radius("geom:point_radius");
|
|
ustring OSLRenderServices::u_point_position("geom:point_position");
|
|
ustring OSLRenderServices::u_point_random("geom:point_random");
|
|
ustring OSLRenderServices::u_normal_map_normal("geom:normal_map_normal");
|
|
ustring OSLRenderServices::u_path_ray_length("path:ray_length");
|
|
ustring OSLRenderServices::u_path_ray_depth("path:ray_depth");
|
|
ustring OSLRenderServices::u_path_diffuse_depth("path:diffuse_depth");
|
|
ustring OSLRenderServices::u_path_glossy_depth("path:glossy_depth");
|
|
ustring OSLRenderServices::u_path_transparent_depth("path:transparent_depth");
|
|
ustring OSLRenderServices::u_path_transmission_depth("path:transmission_depth");
|
|
ustring OSLRenderServices::u_path_portal_depth("path:portal_depth");
|
|
ustring OSLRenderServices::u_trace("trace");
|
|
ustring OSLRenderServices::u_hit("hit");
|
|
ustring OSLRenderServices::u_hitdist("hitdist");
|
|
ustring OSLRenderServices::u_N("N");
|
|
ustring OSLRenderServices::u_Ng("Ng");
|
|
ustring OSLRenderServices::u_P("P");
|
|
ustring OSLRenderServices::u_I("I");
|
|
ustring OSLRenderServices::u_u("u");
|
|
ustring OSLRenderServices::u_v("v");
|
|
ustring OSLRenderServices::u_empty;
|
|
|
|
ustring OSLRenderServices::u_sensor_size("cam:sensor_size");
|
|
ustring OSLRenderServices::u_image_resolution("cam:image_resolution");
|
|
ustring OSLRenderServices::u_aperture_aspect_ratio("cam:aperture_aspect_ratio");
|
|
ustring OSLRenderServices::u_aperture_size("cam:aperture_size");
|
|
ustring OSLRenderServices::u_aperture_position("cam:aperture_position");
|
|
ustring OSLRenderServices::u_focal_distance("cam:focal_distance");
|
|
|
|
ImageManager *OSLRenderServices::image_manager = nullptr;
|
|
|
|
OSLRenderServices::OSLRenderServices(OSL::TextureSystem *texture_system, const int device_type)
|
|
: OSL::RendererServices(texture_system), device_type_(device_type)
|
|
{
|
|
}
|
|
|
|
OSLRenderServices::~OSLRenderServices()
|
|
{
|
|
if (m_texturesys) {
|
|
LOG_INFO << "OSL texture system stats:\n" << m_texturesys->getstats();
|
|
}
|
|
}
|
|
|
|
int OSLRenderServices::supports(string_view feature) const
|
|
{
|
|
#ifdef WITH_OPTIX
|
|
if (feature == "OptiX") {
|
|
return device_type_ == DEVICE_OPTIX;
|
|
}
|
|
#else
|
|
(void)feature;
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
|
|
OSL::Matrix44 &result,
|
|
OSL::TransformationPtr /*xform*/,
|
|
const float time)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
|
|
if (globals == nullptr || globals->sd == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
/* this is only used for shader and object space, we don't really have
|
|
* a concept of shader space, so we just use object space for both. */
|
|
const ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
const int object = sd->object;
|
|
|
|
if (object != OBJECT_NONE) {
|
|
#ifdef __OBJECT_MOTION__
|
|
Transform tfm;
|
|
|
|
if (time == sd->time) {
|
|
tfm = object_get_transform(kg, sd);
|
|
}
|
|
else {
|
|
tfm = object_fetch_transform_motion_test(kg, object, time, nullptr);
|
|
}
|
|
#else
|
|
const Transform tfm = object_get_transform(kg, sd);
|
|
#endif
|
|
copy_matrix(result, tfm);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
|
|
OSL::Matrix44 &result,
|
|
OSL::TransformationPtr /*xform*/,
|
|
const float time)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
|
|
if (globals == nullptr || globals->sd == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
/* this is only used for shader and object space, we don't really have
|
|
* a concept of shader space, so we just use object space for both. */
|
|
const ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
const int object = sd->object;
|
|
|
|
if (object != OBJECT_NONE) {
|
|
#ifdef __OBJECT_MOTION__
|
|
Transform itfm;
|
|
|
|
if (time == sd->time) {
|
|
itfm = object_get_inverse_transform(kg, sd);
|
|
}
|
|
else {
|
|
object_fetch_transform_motion_test(kg, object, time, &itfm);
|
|
}
|
|
#else
|
|
const Transform itfm = object_get_inverse_transform(kg, sd);
|
|
#endif
|
|
copy_matrix(result, itfm);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
|
|
OSL::Matrix44 &result,
|
|
OSLUStringHash from,
|
|
const float /*time*/)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
|
|
if (from == u_ndc) {
|
|
copy_matrix(result, kernel_data.cam.ndctoworld);
|
|
return true;
|
|
}
|
|
if (from == u_raster) {
|
|
copy_matrix(result, kernel_data.cam.rastertoworld);
|
|
return true;
|
|
}
|
|
if (from == u_screen) {
|
|
copy_matrix(result, kernel_data.cam.screentoworld);
|
|
return true;
|
|
}
|
|
if (from == u_camera) {
|
|
copy_matrix(result, kernel_data.cam.cameratoworld);
|
|
return true;
|
|
}
|
|
if (from == u_world) {
|
|
result.makeIdentity();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
|
|
OSL::Matrix44 &result,
|
|
OSLUStringHash to,
|
|
const float /*time*/)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
|
|
if (to == u_ndc) {
|
|
copy_matrix(result, kernel_data.cam.worldtondc);
|
|
return true;
|
|
}
|
|
if (to == u_raster) {
|
|
copy_matrix(result, kernel_data.cam.worldtoraster);
|
|
return true;
|
|
}
|
|
if (to == u_screen) {
|
|
copy_matrix(result, kernel_data.cam.worldtoscreen);
|
|
return true;
|
|
}
|
|
if (to == u_camera) {
|
|
copy_matrix(result, kernel_data.cam.worldtocamera);
|
|
return true;
|
|
}
|
|
if (to == u_world) {
|
|
result.makeIdentity();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
|
|
OSL::Matrix44 &result,
|
|
OSL::TransformationPtr /*xform*/)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
|
|
if (globals == nullptr || globals->sd == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
/* this is only used for shader and object space, we don't really have
|
|
* a concept of shader space, so we just use object space for both. */
|
|
const ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
const int object = sd->object;
|
|
|
|
if (object != OBJECT_NONE) {
|
|
const Transform tfm = object_get_transform(kg, sd);
|
|
copy_matrix(result, tfm);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
|
|
OSL::Matrix44 &result,
|
|
OSL::TransformationPtr /*xform*/)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
|
|
if (globals == nullptr || globals->sd == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
/* this is only used for shader and object space, we don't really have
|
|
* a concept of shader space, so we just use object space for both. */
|
|
const ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
const int object = sd->object;
|
|
|
|
if (object != OBJECT_NONE) {
|
|
const Transform tfm = object_get_inverse_transform(kg, sd);
|
|
copy_matrix(result, tfm);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg,
|
|
OSL::Matrix44 &result,
|
|
OSLUStringHash from)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
|
|
if (from == u_ndc) {
|
|
copy_matrix(result, kernel_data.cam.ndctoworld);
|
|
return true;
|
|
}
|
|
if (from == u_raster) {
|
|
copy_matrix(result, kernel_data.cam.rastertoworld);
|
|
return true;
|
|
}
|
|
if (from == u_screen) {
|
|
copy_matrix(result, kernel_data.cam.screentoworld);
|
|
return true;
|
|
}
|
|
if (from == u_camera) {
|
|
copy_matrix(result, kernel_data.cam.cameratoworld);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg,
|
|
OSL::Matrix44 &result,
|
|
OSLUStringHash to)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
|
|
if (to == u_ndc) {
|
|
copy_matrix(result, kernel_data.cam.worldtondc);
|
|
return true;
|
|
}
|
|
if (to == u_raster) {
|
|
copy_matrix(result, kernel_data.cam.worldtoraster);
|
|
return true;
|
|
}
|
|
if (to == u_screen) {
|
|
copy_matrix(result, kernel_data.cam.worldtoscreen);
|
|
return true;
|
|
}
|
|
if (to == u_camera) {
|
|
copy_matrix(result, kernel_data.cam.worldtocamera);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_array_attribute(OSL::ShaderGlobals * /*sg*/,
|
|
bool /* derivatives*/,
|
|
OSLUStringHash /* object*/,
|
|
const TypeDesc /* type*/,
|
|
OSLUStringHash /* name*/,
|
|
const int /* index*/,
|
|
void * /*val*/)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool set_attribute(
|
|
const T v, const T dx, const T dy, TypeDesc type, bool derivatives, void *val);
|
|
|
|
inline void set_data_float(
|
|
const float v, const float dx, const float dy, bool derivatives, void *val)
|
|
{
|
|
float *fval = static_cast<float *>(val);
|
|
fval[0] = v;
|
|
if (derivatives) {
|
|
fval[1] = dx;
|
|
fval[2] = dy;
|
|
}
|
|
}
|
|
|
|
inline void set_data_float3(
|
|
const float3 v, const float3 dx, const float3 dy, bool derivatives, void *val)
|
|
{
|
|
float *fval = static_cast<float *>(val);
|
|
fval[0] = v.x;
|
|
fval[1] = v.y;
|
|
fval[2] = v.z;
|
|
if (derivatives) {
|
|
fval[3] = dx.x;
|
|
fval[4] = dx.y;
|
|
fval[5] = dx.z;
|
|
fval[6] = dy.x;
|
|
fval[7] = dy.y;
|
|
fval[8] = dy.z;
|
|
}
|
|
}
|
|
|
|
inline void set_data_float4(
|
|
const float4 v, const float4 dx, const float4 dy, bool derivatives, void *val)
|
|
{
|
|
float *fval = static_cast<float *>(val);
|
|
fval[0] = v.x;
|
|
fval[1] = v.y;
|
|
fval[2] = v.z;
|
|
fval[3] = v.w;
|
|
if (derivatives) {
|
|
fval[4] = dx.x;
|
|
fval[5] = dx.y;
|
|
fval[6] = dx.z;
|
|
fval[7] = dx.w;
|
|
fval[8] = dy.x;
|
|
fval[9] = dy.y;
|
|
fval[10] = dy.z;
|
|
fval[11] = dy.w;
|
|
}
|
|
}
|
|
|
|
ccl_device_template_spec bool set_attribute(
|
|
const float v, const float dx, const float dy, TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
if (type == TypeFloatArray4) {
|
|
set_data_float4(make_float4(v, v, v, 1.0f),
|
|
make_float4(dx, dx, dx, 0.0f),
|
|
make_float4(dy, dy, dy, 0.0f),
|
|
derivatives,
|
|
val);
|
|
return true;
|
|
}
|
|
if (type == TypePoint || type == TypeVector || type == TypeNormal || type == TypeColor) {
|
|
set_data_float3(make_float3(v), make_float3(dx), make_float3(dy), derivatives, val);
|
|
return true;
|
|
}
|
|
if (type == TypeFloat) {
|
|
set_data_float(v, dx, dy, derivatives, val);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ccl_device_template_spec bool set_attribute(
|
|
const float2 v, const float2 dx, const float2 dy, TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
if (type == TypeFloatArray4) {
|
|
set_data_float4(make_float4(v.x, v.y, 0.0f, 1.0f),
|
|
make_float4(dx.x, dx.y, 0.0f, 0.0f),
|
|
make_float4(dy.x, dy.y, 0.0f, 0.0f),
|
|
derivatives,
|
|
val);
|
|
return true;
|
|
}
|
|
if (type == TypePoint || type == TypeVector || type == TypeNormal || type == TypeColor) {
|
|
set_data_float3(make_float3(v), make_float3(dx), make_float3(dy), derivatives, val);
|
|
return true;
|
|
}
|
|
if (type == TypeFloat) {
|
|
set_data_float(average(v), average(dx), average(dy), derivatives, val);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ccl_device_template_spec bool set_attribute(
|
|
const float3 v, const float3 dx, const float3 dy, TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
if (type == TypeFloatArray4) {
|
|
set_data_float4(
|
|
make_float4(v, 1.0f), make_float4(dx, 0.0f), make_float4(dy, 0.0f), derivatives, val);
|
|
return true;
|
|
}
|
|
if (type == TypePoint || type == TypeVector || type == TypeNormal || type == TypeColor) {
|
|
set_data_float3(v, dx, dy, derivatives, val);
|
|
return true;
|
|
}
|
|
if (type == TypeFloat) {
|
|
set_data_float(average(v), average(dx), average(dy), derivatives, val);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Attributes with the TypeRGBA type descriptor should be retrieved and stored
|
|
* in a float array of size 4 (e.g. node_vertex_color.osl), this array have
|
|
* a type descriptor TypeFloatArray4. If the storage is not a TypeFloatArray4,
|
|
* we either store the first three components in a vector, store the average of
|
|
* the components in a float, or fail the retrieval and do nothing. We allow
|
|
* this for the correct operation of the Attribute node.
|
|
*/
|
|
|
|
ccl_device_template_spec bool set_attribute(
|
|
const float4 v, const float4 dx, const float4 dy, TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
if (type == TypeFloatArray4) {
|
|
set_data_float4(v, dx, dy, derivatives, val);
|
|
return true;
|
|
}
|
|
if (type == TypePoint || type == TypeVector || type == TypeNormal || type == TypeColor) {
|
|
set_data_float3(make_float3(v), make_float3(dx), make_float3(dy), derivatives, val);
|
|
return true;
|
|
}
|
|
if (type == TypeFloat) {
|
|
set_data_float(average(make_float3(v)),
|
|
average(make_float3(dx)),
|
|
average(make_float3(dy)),
|
|
derivatives,
|
|
val);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<typename T>
|
|
ccl_device_inline bool set_attribute(const T f, const TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
return set_attribute(f, make_zero<T>(), make_zero<T>(), type, derivatives, val);
|
|
}
|
|
|
|
ccl_device_template_spec bool set_attribute(const int i,
|
|
const TypeDesc type,
|
|
bool derivatives,
|
|
void *val)
|
|
{
|
|
if (type.basetype == TypeDesc::INT && type.aggregate == TypeDesc::SCALAR && type.arraylen == 0) {
|
|
int *ival = (int *)val;
|
|
ival[0] = i;
|
|
|
|
if (derivatives) {
|
|
ival[1] = 0;
|
|
ival[2] = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ccl_device_template_spec bool set_attribute(ustring str,
|
|
const TypeDesc type,
|
|
bool derivatives,
|
|
void *val)
|
|
{
|
|
if (type.basetype == TypeDesc::STRING && type.aggregate == TypeDesc::SCALAR &&
|
|
type.arraylen == 0)
|
|
{
|
|
ustring *sval = (ustring *)val;
|
|
sval[0] = str;
|
|
|
|
if (derivatives) {
|
|
sval[1] = OSLRenderServices::u_empty;
|
|
sval[2] = OSLRenderServices::u_empty;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool set_attribute_float3_3(const float3 P[3], TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
if (type.vecsemantics == TypeDesc::POINT && type.arraylen >= 3) {
|
|
float *fval = (float *)val;
|
|
|
|
fval[0] = P[0].x;
|
|
fval[1] = P[0].y;
|
|
fval[2] = P[0].z;
|
|
|
|
fval[3] = P[1].x;
|
|
fval[4] = P[1].y;
|
|
fval[5] = P[1].z;
|
|
|
|
fval[6] = P[2].x;
|
|
fval[7] = P[2].y;
|
|
fval[8] = P[2].z;
|
|
|
|
if (type.arraylen > 3) {
|
|
memset(fval + 3 * 3, 0, sizeof(float) * 3 * (type.arraylen - 3));
|
|
}
|
|
if (derivatives) {
|
|
memset(fval + type.arraylen * 3, 0, sizeof(float) * 2 * 3 * type.arraylen);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool set_attribute_matrix(const Transform &tfm, const TypeDesc type, void *val)
|
|
{
|
|
if (type == TypeMatrix) {
|
|
copy_matrix(*(OSL::Matrix44 *)val, tfm);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool get_object_attribute_impl(const ThreadKernelGlobalsCPU *kg,
|
|
ShaderData *sd,
|
|
const AttributeDescriptor &desc,
|
|
const TypeDesc &type,
|
|
bool derivatives,
|
|
void *val)
|
|
{
|
|
T v;
|
|
T dx = make_zero<T>();
|
|
T dy = make_zero<T>();
|
|
#ifdef __VOLUME__
|
|
if (primitive_is_volume_attribute(sd)) {
|
|
v = primitive_volume_attribute<T>(kg, sd, desc, true);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
v = primitive_surface_attribute<T>(
|
|
kg, sd, desc, derivatives ? &dx : nullptr, derivatives ? &dy : nullptr);
|
|
}
|
|
return set_attribute(v, dx, dy, type, derivatives, val);
|
|
}
|
|
|
|
static bool get_object_attribute(const ThreadKernelGlobalsCPU *kg,
|
|
ShaderData *sd,
|
|
const AttributeDescriptor &desc,
|
|
const TypeDesc &type,
|
|
bool derivatives,
|
|
void *val)
|
|
{
|
|
if (desc.type == NODE_ATTR_FLOAT) {
|
|
return get_object_attribute_impl<float>(kg, sd, desc, type, derivatives, val);
|
|
}
|
|
if (desc.type == NODE_ATTR_FLOAT2) {
|
|
return get_object_attribute_impl<float2>(kg, sd, desc, type, derivatives, val);
|
|
}
|
|
if (desc.type == NODE_ATTR_FLOAT3) {
|
|
return get_object_attribute_impl<float3>(kg, sd, desc, type, derivatives, val);
|
|
}
|
|
if (desc.type == NODE_ATTR_FLOAT4 || desc.type == NODE_ATTR_RGBA) {
|
|
return get_object_attribute_impl<float4>(kg, sd, desc, type, derivatives, val);
|
|
}
|
|
if (desc.type == NODE_ATTR_MATRIX) {
|
|
const Transform tfm = primitive_attribute_matrix(kg, desc);
|
|
return set_attribute_matrix(tfm, type, val);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_object_standard_attribute(
|
|
ShaderGlobals *globals, OSLUStringHash name, const TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
/* todo: turn this into hash table? */
|
|
|
|
/* Object Attributes */
|
|
if (name == u_object_location) {
|
|
const float3 f = object_location(kg, sd);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_object_color) {
|
|
const float3 f = object_color(kg, sd->object);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_object_alpha) {
|
|
const float f = object_alpha(kg, sd->object);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_object_index) {
|
|
const float f = object_pass_id(kg, sd->object);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_object_is_light) {
|
|
const float f = (sd->type & PRIMITIVE_LAMP) != 0;
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_geom_dupli_generated) {
|
|
const float3 f = object_dupli_generated(kg, sd->object);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_geom_dupli_uv) {
|
|
const float3 f = object_dupli_uv(kg, sd->object);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_material_index) {
|
|
const float f = shader_pass_id(kg, sd);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_object_random) {
|
|
const float f = object_random_number(kg, sd->object);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
|
|
/* Particle Attributes */
|
|
if (name == u_particle_index) {
|
|
const int particle_id = object_particle_id(kg, sd->object);
|
|
const float f = particle_index(kg, particle_id);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_particle_random) {
|
|
const int particle_id = object_particle_id(kg, sd->object);
|
|
const float f = hash_uint2_to_float(particle_index(kg, particle_id), 0);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_particle_age) {
|
|
const int particle_id = object_particle_id(kg, sd->object);
|
|
const float f = particle_age(kg, particle_id);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_particle_lifetime) {
|
|
const int particle_id = object_particle_id(kg, sd->object);
|
|
const float f = particle_lifetime(kg, particle_id);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_particle_location) {
|
|
const int particle_id = object_particle_id(kg, sd->object);
|
|
const float3 f = particle_location(kg, particle_id);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
#if 0 /* unsupported */
|
|
if (name == u_particle_rotation) {
|
|
int particle_id = object_particle_id(kg, sd->object);
|
|
float4 f = particle_rotation(kg, particle_id);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
#endif
|
|
if (name == u_particle_size) {
|
|
const int particle_id = object_particle_id(kg, sd->object);
|
|
const float f = particle_size(kg, particle_id);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_particle_velocity) {
|
|
const int particle_id = object_particle_id(kg, sd->object);
|
|
const float3 f = particle_velocity(kg, particle_id);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_particle_angular_velocity) {
|
|
const int particle_id = object_particle_id(kg, sd->object);
|
|
const float3 f = particle_angular_velocity(kg, particle_id);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
|
|
/* Geometry Attributes */
|
|
if (name == u_geom_numpolyvertices) {
|
|
return set_attribute(3, type, derivatives, val);
|
|
}
|
|
if ((name == u_geom_trianglevertices || name == u_geom_polyvertices) &&
|
|
sd->type & PRIMITIVE_TRIANGLE)
|
|
{
|
|
float3 P[3];
|
|
|
|
if (sd->type & PRIMITIVE_MOTION) {
|
|
motion_triangle_vertices(kg, sd->object, sd->prim, sd->time, P);
|
|
}
|
|
else {
|
|
triangle_vertices(kg, sd->prim, P);
|
|
}
|
|
|
|
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
|
object_position_transform(kg, sd, &P[0]);
|
|
object_position_transform(kg, sd, &P[1]);
|
|
object_position_transform(kg, sd, &P[2]);
|
|
}
|
|
|
|
return set_attribute_float3_3(P, type, derivatives, val);
|
|
}
|
|
if (name == u_geom_name) {
|
|
const ustring object_name = kg->osl.globals->object_names[sd->object];
|
|
return set_attribute(object_name, type, derivatives, val);
|
|
}
|
|
if (name == u_is_smooth) {
|
|
const float f = ((sd->shader & SHADER_SMOOTH_NORMAL) != 0);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
#ifdef __HAIR__
|
|
/* Hair Attributes */
|
|
if (name == u_is_curve) {
|
|
const float f = (sd->type & PRIMITIVE_CURVE) != 0;
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_curve_thickness) {
|
|
const float f = curve_thickness(kg, sd);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_curve_tangent_normal) {
|
|
const float3 f = curve_tangent_normal(sd);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_curve_random) {
|
|
const float f = curve_random(kg, sd);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
#endif
|
|
#ifdef __POINTCLOUD__
|
|
/* point attributes */
|
|
if (name == u_is_point) {
|
|
const float f = (sd->type & PRIMITIVE_POINT) != 0;
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_point_radius) {
|
|
const float f = point_radius(kg, sd);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_point_position) {
|
|
const float3 f = point_position(kg, sd);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_point_random) {
|
|
const float f = point_random(kg, sd);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
#endif
|
|
if (name == u_normal_map_normal) {
|
|
if (sd->type & PRIMITIVE_TRIANGLE) {
|
|
const float3 f = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
return false;
|
|
}
|
|
if (name == u_bump_map_normal) {
|
|
float3 f[3];
|
|
if (!attribute_bump_map_normal(kg, sd, f)) {
|
|
return false;
|
|
}
|
|
return set_attribute(f[0], f[1], f[2], type, derivatives, val);
|
|
}
|
|
return get_background_attribute(globals, name, type, derivatives, val);
|
|
}
|
|
|
|
bool OSLRenderServices::get_background_attribute(
|
|
ShaderGlobals *globals, OSLUStringHash name, const TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
const IntegratorStateCPU *state = globals->path_state;
|
|
const IntegratorShadowStateCPU *shadow_state = globals->shadow_path_state;
|
|
if (name == u_path_ray_length) {
|
|
/* Ray Length */
|
|
const float f = sd->ray_length;
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
|
|
#define READ_PATH_STATE(elem) \
|
|
((state != nullptr) ? state->path.elem : \
|
|
(shadow_state != nullptr) ? shadow_state->shadow_path.elem : \
|
|
0)
|
|
|
|
if (name == u_path_ray_depth) {
|
|
/* Ray Depth */
|
|
int f = READ_PATH_STATE(bounce);
|
|
|
|
/* Read bounce from different locations depending on if this is a shadow path. For background,
|
|
* light emission and shadow evaluation from a surface or volume we are effectively one bounce
|
|
* further. */
|
|
if (globals->raytype & (PATH_RAY_SHADOW | PATH_RAY_EMISSION)) {
|
|
f += 1;
|
|
}
|
|
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_path_diffuse_depth) {
|
|
/* Diffuse Ray Depth */
|
|
const int f = READ_PATH_STATE(diffuse_bounce);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_path_glossy_depth) {
|
|
/* Glossy Ray Depth */
|
|
const int f = READ_PATH_STATE(glossy_bounce);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_path_transmission_depth) {
|
|
/* Transmission Ray Depth */
|
|
const int f = READ_PATH_STATE(transmission_bounce);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_path_transparent_depth) {
|
|
/* Transparent Ray Depth */
|
|
const int f = READ_PATH_STATE(transparent_bounce);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
if (name == u_path_portal_depth) {
|
|
/* Portal Ray Depth */
|
|
const int f = READ_PATH_STATE(portal_bounce);
|
|
return set_attribute(f, type, derivatives, val);
|
|
}
|
|
#undef READ_PATH_STATE
|
|
|
|
if (name == u_ndc) {
|
|
/* NDC coordinates with special exception for orthographic projection. */
|
|
float3 ndc[3];
|
|
|
|
if ((globals->raytype & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE &&
|
|
kernel_data.cam.type == CAMERA_ORTHOGRAPHIC)
|
|
{
|
|
ndc[0] = camera_world_to_ndc(kg, sd, sd->ray_P);
|
|
|
|
if (derivatives) {
|
|
ndc[1] = zero_float3();
|
|
ndc[2] = zero_float3();
|
|
}
|
|
}
|
|
else {
|
|
ndc[0] = camera_world_to_ndc(kg, sd, sd->P);
|
|
|
|
if (derivatives) {
|
|
const differential3 dP = differential_from_compact(sd->Ng, sd->dP);
|
|
ndc[1] = camera_world_to_ndc(kg, sd, sd->P + dP.dx) - ndc[0];
|
|
ndc[2] = camera_world_to_ndc(kg, sd, sd->P + dP.dy) - ndc[0];
|
|
}
|
|
}
|
|
|
|
return set_attribute(ndc[0], ndc[1], ndc[2], type, derivatives, val);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_camera_attribute(
|
|
ShaderGlobals *globals, OSLUStringHash name, TypeDesc type, bool derivatives, void *val)
|
|
{
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
if (name == u_sensor_size) {
|
|
const float2 sensor = make_float2(kernel_data.cam.sensorwidth, kernel_data.cam.sensorheight);
|
|
return set_attribute(sensor, type, derivatives, val);
|
|
}
|
|
if (name == u_image_resolution) {
|
|
const float2 image = make_float2(kernel_data.cam.width, kernel_data.cam.height);
|
|
return set_attribute(image, type, derivatives, val);
|
|
}
|
|
if (name == u_aperture_aspect_ratio) {
|
|
return set_attribute(1.0f / kernel_data.cam.inv_aperture_ratio, type, derivatives, val);
|
|
}
|
|
if (name == u_aperture_size) {
|
|
return set_attribute(kernel_data.cam.aperturesize, type, derivatives, val);
|
|
}
|
|
if (name == u_aperture_position) {
|
|
/* The random numbers for aperture sampling are packed into N. */
|
|
const float2 rand_lens = make_float2(globals->N.x, globals->N.y);
|
|
const float2 pos = camera_sample_aperture(&kernel_data.cam, rand_lens);
|
|
return set_attribute(pos * kernel_data.cam.aperturesize, type, derivatives, val);
|
|
}
|
|
if (name == u_focal_distance) {
|
|
return set_attribute(kernel_data.cam.focaldistance, type, derivatives, val);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::get_attribute(OSL::ShaderGlobals *sg,
|
|
bool derivatives,
|
|
OSLUStringHash object_name,
|
|
const TypeDesc type,
|
|
OSLUStringHash name,
|
|
void *val)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
if (globals == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
if (sd == nullptr) {
|
|
/* Camera shader. */
|
|
return get_camera_attribute(globals, name, type, derivatives, val);
|
|
}
|
|
|
|
/* lookup of attribute on another object */
|
|
int object;
|
|
if (object_name != u_empty) {
|
|
const OSLGlobals::ObjectNameMap::iterator it = kg->osl.globals->object_name_map.find(
|
|
object_name);
|
|
|
|
if (it == kg->osl.globals->object_name_map.end()) {
|
|
return false;
|
|
}
|
|
|
|
object = it->second;
|
|
}
|
|
else {
|
|
object = sd->object;
|
|
}
|
|
|
|
/* find attribute on object */
|
|
const AttributeDescriptor desc = find_attribute(kg, object, sd->prim, name.hash());
|
|
if (desc.offset != ATTR_STD_NOT_FOUND) {
|
|
return get_object_attribute(kg, sd, desc, type, derivatives, val);
|
|
}
|
|
|
|
/* not found in attribute, check standard object info */
|
|
return get_object_standard_attribute(globals, name, type, derivatives, val);
|
|
}
|
|
|
|
bool OSLRenderServices::get_userdata(bool /*derivatives*/,
|
|
OSLUStringHash /* name*/,
|
|
const TypeDesc /* type*/,
|
|
OSL::ShaderGlobals * /*sg*/,
|
|
void * /*val*/)
|
|
{
|
|
return false; /* disabled by lockgeom */
|
|
}
|
|
|
|
OSL::TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(
|
|
OSLUStringHash filename, OSL::ShadingContext *context, const OSL::TextureOpt *opt)
|
|
{
|
|
return get_texture_handle(to_ustring(filename), context, opt);
|
|
}
|
|
|
|
OSL::TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(
|
|
OSL::ustring filename, OSL::ShadingContext * /*context*/, const OSL::TextureOpt * /*options*/)
|
|
{
|
|
OSLTextureHandleMap::iterator it = textures.find(filename);
|
|
|
|
if (device_type_ == DEVICE_CPU) {
|
|
/* For non-OIIO textures, just return a pointer to our own OSLTextureHandle. */
|
|
if (it != textures.end()) {
|
|
if (it->second.type != OSLTextureHandle::OIIO) {
|
|
return (OSL::TextureSystem::TextureHandle *)(&it->second);
|
|
}
|
|
}
|
|
|
|
/* Get handle from OpenImageIO. */
|
|
OSL::TextureSystem *ts = m_texturesys;
|
|
OSL::TextureSystem::TextureHandle *handle = ts->get_texture_handle(to_ustring(filename));
|
|
if (handle == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* Insert new OSLTextureHandle if needed. */
|
|
if (it == textures.end()) {
|
|
textures.insert(filename, OSLTextureHandle(OSLTextureHandle::OIIO));
|
|
it = textures.find(filename);
|
|
}
|
|
|
|
/* Assign OIIO texture handle and return.
|
|
* OIIO::unordered_map_concurrent always returns a const handle even if the underlying
|
|
* std::unordered_map supports updating values just fine. */
|
|
const_cast<OSLTextureHandle &>(it->second).oiio_handle = handle;
|
|
return (OSL::TextureSystem::TextureHandle *)(&it->second);
|
|
}
|
|
|
|
/* Construct GPU texture handle for existing textures. */
|
|
if (it != textures.end()) {
|
|
switch (it->second.type) {
|
|
case OSLTextureHandle::OIIO:
|
|
return nullptr;
|
|
case OSLTextureHandle::SVM:
|
|
if (!it->second.handle.empty() && it->second.handle.get_manager() != image_manager) {
|
|
it.clear();
|
|
break;
|
|
}
|
|
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_SVM |
|
|
it->second.svm_slots[0].y);
|
|
case OSLTextureHandle::IES:
|
|
if (!it->second.handle.empty() && it->second.handle.get_manager() != image_manager) {
|
|
it.clear();
|
|
break;
|
|
}
|
|
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_IES |
|
|
it->second.svm_slots[0].y);
|
|
case OSLTextureHandle::AO:
|
|
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(
|
|
OSL_TEXTURE_HANDLE_TYPE_AO_OR_BEVEL | 1);
|
|
case OSLTextureHandle::BEVEL:
|
|
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(
|
|
OSL_TEXTURE_HANDLE_TYPE_AO_OR_BEVEL | 2);
|
|
}
|
|
}
|
|
|
|
if (!image_manager) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* Load new textures using SVM image manager. */
|
|
const ImageHandle handle = image_manager->add_image(filename.string(), ImageParams());
|
|
if (handle.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!textures.insert(filename, OSLTextureHandle(handle))) {
|
|
return nullptr;
|
|
}
|
|
|
|
return reinterpret_cast<OSL::TextureSystem::TextureHandle *>(OSL_TEXTURE_HANDLE_TYPE_SVM |
|
|
handle.svm_slot());
|
|
}
|
|
|
|
bool OSLRenderServices::good(OSL::TextureSystem::TextureHandle *texture_handle)
|
|
{
|
|
OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle;
|
|
|
|
if (handle->oiio_handle) {
|
|
OSL::TextureSystem *ts = m_texturesys;
|
|
return ts->good(handle->oiio_handle);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OSLRenderServices::texture(OSLUStringHash filename,
|
|
TextureHandle *texture_handle,
|
|
TexturePerthread *texture_thread_info,
|
|
OSL::TextureOpt &options,
|
|
OSL::ShaderGlobals *sg,
|
|
float s,
|
|
float t,
|
|
const float dsdx,
|
|
const float dtdx,
|
|
const float dsdy,
|
|
const float dtdy,
|
|
const int nchannels,
|
|
float *result,
|
|
float *dresultds,
|
|
float *dresultdt,
|
|
OSLUStringHash * /*errormessage*/)
|
|
{
|
|
OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle;
|
|
const OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO;
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kernel_globals = globals->kg;
|
|
const IntegratorStateCPU *state = globals->path_state;
|
|
bool status = false;
|
|
|
|
switch (texture_type) {
|
|
case OSLTextureHandle::BEVEL: {
|
|
#ifdef __SHADER_RAYTRACE__
|
|
/* Bevel shader hack. */
|
|
if (nchannels >= 3 && state != nullptr) {
|
|
const int num_samples = (int)s;
|
|
const float radius = t;
|
|
const float3 N = svm_bevel(kernel_globals, state, sd, radius, num_samples);
|
|
result[0] = N.x;
|
|
result[1] = N.y;
|
|
result[2] = N.z;
|
|
status = true;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
case OSLTextureHandle::AO: {
|
|
#ifdef __SHADER_RAYTRACE__
|
|
/* AO shader hack. */
|
|
if (state != nullptr) {
|
|
const int num_samples = (int)s;
|
|
const float radius = t;
|
|
const float3 N = make_float3(dsdx, dtdx, dsdy);
|
|
int flags = 0;
|
|
if ((int)dtdy) {
|
|
flags |= NODE_AO_INSIDE;
|
|
}
|
|
if ((int)options.sblur) {
|
|
flags |= NODE_AO_ONLY_LOCAL;
|
|
}
|
|
if ((int)options.tblur) {
|
|
flags |= NODE_AO_GLOBAL_RADIUS;
|
|
}
|
|
result[0] = svm_ao(kernel_globals, state, sd, N, radius, num_samples, flags);
|
|
status = true;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
case OSLTextureHandle::SVM: {
|
|
int id = -1;
|
|
if (handle->svm_slots[0].w == -1) {
|
|
/* Packed single texture. */
|
|
id = handle->svm_slots[0].y;
|
|
}
|
|
else {
|
|
/* Packed tiled texture. */
|
|
const int tx = (int)s;
|
|
const int ty = (int)t;
|
|
const int tile = 1001 + 10 * ty + tx;
|
|
for (const int4 &tile_node : handle->svm_slots) {
|
|
if (tile_node.x == tile) {
|
|
id = tile_node.y;
|
|
break;
|
|
}
|
|
if (tile_node.z == tile) {
|
|
id = tile_node.w;
|
|
break;
|
|
}
|
|
}
|
|
s -= tx;
|
|
t -= ty;
|
|
}
|
|
|
|
float4 rgba;
|
|
if (id == -1) {
|
|
rgba = make_float4(
|
|
TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
|
|
}
|
|
else {
|
|
rgba = kernel_tex_image_interp(kernel_globals, id, s, 1.0f - t);
|
|
}
|
|
|
|
result[0] = rgba[0];
|
|
if (nchannels > 1) {
|
|
result[1] = rgba[1];
|
|
}
|
|
if (nchannels > 2) {
|
|
result[2] = rgba[2];
|
|
}
|
|
if (nchannels > 3) {
|
|
result[3] = rgba[3];
|
|
}
|
|
status = true;
|
|
break;
|
|
}
|
|
case OSLTextureHandle::IES: {
|
|
/* IES light. */
|
|
result[0] = kernel_ies_interp(kernel_globals, handle->svm_slots[0].y, s, t);
|
|
status = true;
|
|
break;
|
|
}
|
|
case OSLTextureHandle::OIIO: {
|
|
/* OpenImageIO texture cache. */
|
|
OSL::TextureSystem *ts = m_texturesys;
|
|
|
|
if (handle && handle->oiio_handle) {
|
|
if (texture_thread_info == nullptr) {
|
|
texture_thread_info = kernel_globals->osl.oiio_thread_info;
|
|
}
|
|
|
|
status = ts->texture(handle->oiio_handle,
|
|
texture_thread_info,
|
|
options,
|
|
s,
|
|
t,
|
|
dsdx,
|
|
dtdx,
|
|
dsdy,
|
|
dtdy,
|
|
nchannels,
|
|
result,
|
|
dresultds,
|
|
dresultdt);
|
|
}
|
|
else {
|
|
status = ts->texture(to_ustring(filename),
|
|
options,
|
|
s,
|
|
t,
|
|
dsdx,
|
|
dtdx,
|
|
dsdy,
|
|
dtdy,
|
|
nchannels,
|
|
result,
|
|
dresultds,
|
|
dresultdt);
|
|
}
|
|
|
|
if (!status) {
|
|
/* This might be slow, but prevents error messages leak and
|
|
* other nasty stuff happening. */
|
|
ts->geterror();
|
|
}
|
|
else if (handle && handle->processor) {
|
|
ColorSpaceManager::to_scene_linear(handle->processor, result, nchannels);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!status) {
|
|
if (nchannels == 3 || nchannels == 4) {
|
|
result[0] = 1.0f;
|
|
result[1] = 0.0f;
|
|
result[2] = 1.0f;
|
|
|
|
if (nchannels == 4) {
|
|
result[3] = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
bool OSLRenderServices::texture3d(OSLUStringHash filename,
|
|
TextureHandle *texture_handle,
|
|
TexturePerthread *texture_thread_info,
|
|
OSL::TextureOpt &options,
|
|
OSL::ShaderGlobals *sg,
|
|
const OSL::Vec3 &P,
|
|
const OSL::Vec3 &dPdx,
|
|
const OSL::Vec3 &dPdy,
|
|
const OSL::Vec3 &dPdz,
|
|
const int nchannels,
|
|
float *result,
|
|
float *dresultds,
|
|
float *dresultdt,
|
|
float *dresultdr,
|
|
OSLUStringHash * /*errormessage*/)
|
|
{
|
|
OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle;
|
|
const OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO;
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
const ThreadKernelGlobalsCPU *kernel_globals = globals->kg;
|
|
bool status = false;
|
|
|
|
switch (texture_type) {
|
|
case OSLTextureHandle::SVM: {
|
|
/* Packed texture. */
|
|
const int slot = handle->svm_slots[0].y;
|
|
const float3 P_float3 = make_float3(P.x, P.y, P.z);
|
|
float4 rgba = kernel_tex_image_interp_3d(
|
|
kernel_globals, slot, P_float3, INTERPOLATION_NONE, -1.0f);
|
|
|
|
result[0] = rgba[0];
|
|
if (nchannels > 1) {
|
|
result[1] = rgba[1];
|
|
}
|
|
if (nchannels > 2) {
|
|
result[2] = rgba[2];
|
|
}
|
|
if (nchannels > 3) {
|
|
result[3] = rgba[3];
|
|
}
|
|
status = true;
|
|
break;
|
|
}
|
|
case OSLTextureHandle::OIIO: {
|
|
/* OpenImageIO texture cache. */
|
|
OSL::TextureSystem *ts = m_texturesys;
|
|
|
|
if (handle && handle->oiio_handle) {
|
|
if (texture_thread_info == nullptr) {
|
|
texture_thread_info = kernel_globals->osl.oiio_thread_info;
|
|
}
|
|
|
|
status = ts->texture3d(handle->oiio_handle,
|
|
texture_thread_info,
|
|
options,
|
|
P,
|
|
dPdx,
|
|
dPdy,
|
|
dPdz,
|
|
nchannels,
|
|
result,
|
|
dresultds,
|
|
dresultdt,
|
|
dresultdr);
|
|
}
|
|
else {
|
|
status = ts->texture3d(to_ustring(filename),
|
|
options,
|
|
P,
|
|
dPdx,
|
|
dPdy,
|
|
dPdz,
|
|
nchannels,
|
|
result,
|
|
dresultds,
|
|
dresultdt,
|
|
dresultdr);
|
|
}
|
|
|
|
if (!status) {
|
|
/* This might be slow, but prevents error messages leak and
|
|
* other nasty stuff happening. */
|
|
ts->geterror();
|
|
}
|
|
else if (handle && handle->processor) {
|
|
ColorSpaceManager::to_scene_linear(handle->processor, result, nchannels);
|
|
}
|
|
break;
|
|
}
|
|
case OSLTextureHandle::IES:
|
|
case OSLTextureHandle::AO:
|
|
case OSLTextureHandle::BEVEL: {
|
|
status = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!status) {
|
|
if (nchannels == 3 || nchannels == 4) {
|
|
result[0] = 1.0f;
|
|
result[1] = 0.0f;
|
|
result[2] = 1.0f;
|
|
|
|
if (nchannels == 4) {
|
|
result[3] = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
bool OSLRenderServices::environment(OSLUStringHash filename,
|
|
TextureHandle *texture_handle,
|
|
TexturePerthread *thread_info,
|
|
OSL::TextureOpt &options,
|
|
OSL::ShaderGlobals *sg,
|
|
const OSL::Vec3 &R,
|
|
const OSL::Vec3 &dRdx,
|
|
const OSL::Vec3 &dRdy,
|
|
const int nchannels,
|
|
float *result,
|
|
float *dresultds,
|
|
float *dresultdt,
|
|
OSLUStringHash * /*errormessage*/)
|
|
{
|
|
OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle;
|
|
OSL::TextureSystem *ts = m_texturesys;
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
bool status = false;
|
|
|
|
if (handle && handle->oiio_handle) {
|
|
if (thread_info == nullptr) {
|
|
thread_info = globals->kg->osl.oiio_thread_info;
|
|
}
|
|
|
|
status = ts->environment(handle->oiio_handle,
|
|
thread_info,
|
|
options,
|
|
R,
|
|
dRdx,
|
|
dRdy,
|
|
nchannels,
|
|
result,
|
|
dresultds,
|
|
dresultdt);
|
|
}
|
|
else {
|
|
status = ts->environment(
|
|
to_ustring(filename), options, R, dRdx, dRdy, nchannels, result, dresultds, dresultdt);
|
|
}
|
|
|
|
if (!status) {
|
|
if (nchannels == 3 || nchannels == 4) {
|
|
result[0] = 1.0f;
|
|
result[1] = 0.0f;
|
|
result[2] = 1.0f;
|
|
|
|
if (nchannels == 4) {
|
|
result[3] = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else if (handle && handle->processor) {
|
|
ColorSpaceManager::to_scene_linear(handle->processor, result, nchannels);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
bool OSLRenderServices::get_texture_info(OSLUStringHash filename,
|
|
TextureHandle *texture_handle,
|
|
TexturePerthread *texture_thread_info,
|
|
OSL::ShaderGlobals * /*sg*/,
|
|
const int subimage,
|
|
OSLUStringHash dataname,
|
|
const TypeDesc datatype,
|
|
void *data,
|
|
OSLUStringHash * /*errormessage*/)
|
|
{
|
|
OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle;
|
|
OSL::TextureSystem *ts = m_texturesys;
|
|
|
|
if (handle) {
|
|
/* No texture info for other texture types. */
|
|
if (handle->type != OSLTextureHandle::OIIO) {
|
|
return false;
|
|
}
|
|
|
|
if (handle->oiio_handle) {
|
|
/* Get texture info from OpenImageIO. */
|
|
return ts->get_texture_info(handle->oiio_handle,
|
|
texture_thread_info,
|
|
subimage,
|
|
to_ustring(dataname),
|
|
datatype,
|
|
data);
|
|
}
|
|
}
|
|
|
|
/* Get texture info from OpenImageIO, slower using filename. */
|
|
return ts->get_texture_info(
|
|
to_ustring(filename), subimage, to_ustring(dataname), datatype, data);
|
|
}
|
|
|
|
int OSLRenderServices::pointcloud_search(OSL::ShaderGlobals * /*sg*/,
|
|
OSLUStringHash /*filename*/,
|
|
const OSL::Vec3 & /*center*/,
|
|
const float /*radius*/,
|
|
const int /*max_points*/,
|
|
bool /*sort*/,
|
|
#if OSL_LIBRARY_VERSION_CODE >= 11400
|
|
int * /*indices*/,
|
|
#else
|
|
size_t * /*out_indices*/,
|
|
#endif
|
|
float * /*out_distances*/,
|
|
const int /*derivs_offset*/)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int OSLRenderServices::pointcloud_get(OSL::ShaderGlobals * /*sg*/
|
|
,
|
|
OSLUStringHash /*filename*/,
|
|
#if OSL_LIBRARY_VERSION_CODE >= 11400
|
|
const int * /*indices*/,
|
|
#else
|
|
size_t * /*indices*/,
|
|
#endif
|
|
const int /*count*/,
|
|
OSLUStringHash /*attr_name*/,
|
|
const TypeDesc /*attr_type*/,
|
|
void * /*out_data*/)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool OSLRenderServices::pointcloud_write(OSL::ShaderGlobals * /*sg*/,
|
|
OSLUStringHash /*filename*/,
|
|
const OSL::Vec3 & /*pos*/,
|
|
const int /*nattribs*/,
|
|
const OSLUStringRep * /*names*/,
|
|
const TypeDesc * /*types*/,
|
|
const void ** /*data*/)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool OSLRenderServices::trace(TraceOpt &options,
|
|
OSL::ShaderGlobals *sg,
|
|
const OSL::Vec3 &P,
|
|
const OSL::Vec3 &dPdx,
|
|
const OSL::Vec3 &dPdy,
|
|
const OSL::Vec3 &R,
|
|
const OSL::Vec3 &dRdx,
|
|
const OSL::Vec3 &dRdy)
|
|
{
|
|
/* todo: options.shader support, maybe options.traceset */
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
ShaderData *sd = globals->sd;
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
|
|
if (sd == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
/* setup ray */
|
|
Ray ray;
|
|
|
|
ray.P = make_float3(P.x, P.y, P.z);
|
|
ray.D = make_float3(R.x, R.y, R.z);
|
|
ray.tmin = 0.0f;
|
|
ray.tmax = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist;
|
|
ray.time = sd->time;
|
|
ray.self.object = OBJECT_NONE;
|
|
ray.self.prim = PRIM_NONE;
|
|
ray.self.light_object = OBJECT_NONE;
|
|
ray.self.light_prim = PRIM_NONE;
|
|
|
|
if (options.mindist == 0.0f) {
|
|
/* avoid self-intersections */
|
|
if (ray.P == sd->P) {
|
|
ray.self.object = sd->object;
|
|
ray.self.prim = sd->prim;
|
|
}
|
|
}
|
|
else {
|
|
/* offset for minimum distance */
|
|
ray.P += options.mindist * ray.D;
|
|
}
|
|
|
|
/* ray differentials */
|
|
differential3 dP;
|
|
dP.dx = make_float3(dPdx.x, dPdx.y, dPdx.z);
|
|
dP.dy = make_float3(dPdy.x, dPdy.y, dPdy.z);
|
|
ray.dP = differential_make_compact(dP);
|
|
differential3 dD;
|
|
dD.dx = make_float3(dRdx.x, dRdx.y, dRdx.z);
|
|
dD.dy = make_float3(dRdy.x, dRdy.y, dRdy.z);
|
|
ray.dD = differential_make_compact(dD);
|
|
|
|
/* allocate trace data */
|
|
OSLTraceData *tracedata = globals->tracedata;
|
|
tracedata->ray = ray;
|
|
tracedata->setup = false;
|
|
tracedata->init = true;
|
|
tracedata->hit = false;
|
|
|
|
/* Can't ray-trace from shaders like displacement, before BVH exists. */
|
|
if (kernel_data.bvh.bvh_layout == BVH_LAYOUT_NONE) {
|
|
return false;
|
|
}
|
|
|
|
/* Ray-trace, leaving out shadow opaque to avoid early exit. */
|
|
const uint visibility = PATH_RAY_ALL_VISIBILITY - PATH_RAY_SHADOW_OPAQUE;
|
|
tracedata->hit = scene_intersect(kg, &ray, visibility, &tracedata->isect);
|
|
return tracedata->hit;
|
|
}
|
|
|
|
bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg,
|
|
OSLUStringHash source,
|
|
OSLUStringHash name,
|
|
const TypeDesc type,
|
|
void *val,
|
|
bool derivatives)
|
|
{
|
|
ShaderGlobals *globals = reinterpret_cast<ShaderGlobals *>(sg);
|
|
const ThreadKernelGlobalsCPU *kg = globals->kg;
|
|
OSLTraceData *tracedata = globals->tracedata;
|
|
|
|
if (source == u_trace && tracedata->init) {
|
|
if (name == u_hit) {
|
|
return set_attribute<int>(tracedata->hit, type, derivatives, val);
|
|
}
|
|
if (tracedata->hit) {
|
|
if (name == u_hitdist) {
|
|
return set_attribute(tracedata->isect.t, type, derivatives, val);
|
|
}
|
|
|
|
ShaderData *sd = &tracedata->sd;
|
|
|
|
if (!tracedata->setup) {
|
|
/* lazy shader data setup */
|
|
shader_setup_from_ray(kg, sd, &tracedata->ray, &tracedata->isect);
|
|
tracedata->setup = true;
|
|
}
|
|
|
|
if (name == u_N) {
|
|
return set_attribute(sd->N, type, derivatives, val);
|
|
}
|
|
if (name == u_Ng) {
|
|
return set_attribute(sd->Ng, type, derivatives, val);
|
|
}
|
|
if (name == u_P) {
|
|
const differential3 dP = differential_from_compact(sd->Ng, sd->dP);
|
|
return set_attribute(sd->P, dP.dx, dP.dy, type, derivatives, val);
|
|
}
|
|
if (name == u_I) {
|
|
const differential3 dI = differential_from_compact(sd->wi, sd->dI);
|
|
return set_attribute(sd->wi, dI.dx, dI.dy, type, derivatives, val);
|
|
}
|
|
if (name == u_u) {
|
|
return set_attribute(sd->u, sd->du.dx, sd->du.dy, type, derivatives, val);
|
|
}
|
|
if (name == u_v) {
|
|
return set_attribute(sd->v, sd->dv.dx, sd->dv.dy, type, derivatives, val);
|
|
}
|
|
|
|
return get_attribute(sg, derivatives, u_empty, type, name, val);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|