bool OSLInput::read_native_tiles ( #if OIIO_PLUGIN_VERSION >= 21 int subimage, int miplevel, #endif int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, void *data) { #if OIIO_PLUGIN_VERSION >= 21 lock_guard lock (m_mutex); if (! seek_subimage (subimage, miplevel)) return false; #endif // Create an ImageBuf wrapper of the user's data ImageSpec spec = m_spec; // Make a spec that describes just this scanline spec.x = xbegin; spec.y = ybegin; spec.z = zbegin; spec.width = xend-xbegin; spec.height = yend-ybegin; spec.depth = zend-zbegin; ImageBuf ibwrapper (spec, data); // Now run the shader on the ImageBuf pixels, which really point to // the caller's data buffer. ASSERT (m_group.get()); ROI roi (spec.x, spec.x+spec.width, spec.y, spec.y+spec.height, spec.z, spec.z+spec.depth); return shade_image (*shadingsys, *m_group, NULL, ibwrapper, m_outputs, ShadePixelCenters, roi, 1); }
bool OSLInput::read_native_scanlines (int ybegin, int yend, int z, void *data) { // Create an ImageBuf wrapper of the user's data ImageSpec spec = m_spec; // Make a spec that describes just this scanline spec.y = ybegin; spec.z = z; spec.height = yend-ybegin; spec.depth = 1; ImageBuf ibwrapper (spec, data); // Now run the shader on the ImageBuf pixels, which really point to // the caller's data buffer. ASSERT (m_group.get()); ROI roi (spec.x, spec.x+spec.width, spec.y, spec.y+spec.height, spec.z, spec.z+spec.depth); return shade_image (*shadingsys, *m_group, NULL, ibwrapper, m_outputs, ShadePixelCenters, roi, 1); }
bool OSLInput::open (const std::string &name, ImageSpec &newspec, const ImageSpec &config) { // std::cout << "OSLInput::open \"" << name << "\"\n"; setup_shadingsys (); std::vector<std::pair<string_view,string_view> > args; string_view shadername = deconstruct_uri (name, &args); if (shadername.empty()) return false; if (! Strutil::ends_with (shadername, ".osl") && ! Strutil::ends_with (shadername, ".oso") && ! Strutil::ends_with (shadername, ".oslgroup") && ! Strutil::ends_with (shadername, ".oslbody")) return false; m_filename = name; m_topspec = ImageSpec (1024, 1024, 4, TypeDesc::FLOAT); // std::cout << " name = " << shadername << " args? " << args.size() << "\n"; for (size_t i = 0; i < args.size(); ++i) { // std::cout << " " << args[i].first << " = " << args[i].second << "\n"; if (args[i].first == "RES") { parse_res (args[i].second, m_topspec.width, m_topspec.height, m_topspec.depth); } else if (args[i].first == "TILE" || args[i].first == "TILES") { parse_res (args[i].second, m_topspec.tile_width, m_topspec.tile_height, m_topspec.tile_depth); } else if (args[i].first == "OUTPUT") { m_outputs.emplace_back(args[i].second); } else if (args[i].first == "MIP") { m_mip = Strutil::from_string<int>(args[i].second); } else if (args[i].first.size() && args[i].second.size()) { parse_param (args[i].first, args[i].second, m_topspec); } } if (m_outputs.empty()) { m_outputs.emplace_back("result"); m_outputs.emplace_back("alpha"); } m_topspec.full_x = m_topspec.x; m_topspec.full_y = m_topspec.y; m_topspec.full_z = m_topspec.z; m_topspec.full_width = m_topspec.width; m_topspec.full_height = m_topspec.height; m_topspec.full_depth = m_topspec.depth; bool ok = true; if (Strutil::ends_with (shadername, ".oslgroup")) { // Serialized group // No further processing necessary std::string groupspec; if (! OIIO::Filesystem::read_text_file (shadername, groupspec)) { // If it didn't name a disk file, assume it's the "inline" // serialized group. groupspec = groupspec.substr (0, groupspec.size()-9); } // std::cout << "Processing group specification:\n---\n" // << groupspec << "\n---\n"; OIIO::lock_guard lock (shading_mutex); m_group = shadingsys->ShaderGroupBegin ("", "surface", groupspec); if (! m_group) return false; // Failed shadingsys->ShaderGroupEnd (); } if (Strutil::ends_with (shadername, ".oso")) { // Compiled shader OIIO::lock_guard lock (shading_mutex); shadername.remove_suffix (4); m_group = shadingsys->ShaderGroupBegin (); for (size_t p = 0, np = m_topspec.extra_attribs.size(); p < np; ++p) { const ParamValue &pv (m_topspec.extra_attribs[p]); shadingsys->Parameter (pv.name(), pv.type(), pv.data(), pv.interp() == ParamValue::INTERP_CONSTANT); } if (! shadingsys->Shader ("surface", shadername, "" /*layername*/ )) { error ("y %s", errhandler.haserror() ? errhandler.geterror() : std::string("OSL error")); ok = false; } shadingsys->ShaderGroupEnd (); } if (Strutil::ends_with (shadername, ".osl")) { // shader source } if (Strutil::ends_with (shadername, ".oslbody")) { // shader source OIIO::lock_guard lock (shading_mutex); shadername.remove_suffix (8); static int exprcount = 0; std::string exprname = OIIO::Strutil::format("expr_%d", exprcount++); std::string sourcecode = "shader " + exprname + " (\n" " float s = u [[ int lockgeom=0 ]],\n" " float t = v [[ int lockgeom=0 ]],\n" " output color result = 0,\n" " output float alpha = 1,\n" " )\n" "{\n" " " + std::string(shadername) + "\n" " ;\n" "}\n"; // std::cout << "Expression-based shader text is:\n---\n" // << sourcecode << "---\n"; std::string err; if (! compile_buffer (sourcecode, exprname, err)) { error ("%s", err); return false; } m_group = shadingsys->ShaderGroupBegin (); for (size_t p = 0, np = m_topspec.extra_attribs.size(); p < np; ++p) { const ParamValue &pv (m_topspec.extra_attribs[p]); shadingsys->Parameter (pv.name(), pv.type(), pv.data(), pv.interp() == ParamValue::INTERP_CONSTANT); } shadingsys->Shader ("surface", exprname, "" /*layername*/) ; shadingsys->ShaderGroupEnd (); } if (!ok || m_group.get() == NULL) return false; shadingsys->attribute (m_group.get(), "renderer_outputs", TypeDesc(TypeDesc::STRING,m_outputs.size()), &m_outputs[0]); #if OIIO_PLUGIN_VERSION < 21 return ok && seek_subimage (0, 0, newspec); #else ok &= seek_subimage (0, 0); if (ok) newspec = spec(); else close (); return ok; #endif }
extern "C" OSL_DLL_EXPORT int test_shade (int argc, const char *argv[]) { OIIO::Timer timer; // Create a new shading system. We pass it the RendererServices // object that services callbacks from the shading system, NULL for // the TextureSystem (that just makes 'create' make its own TS), and // an error handler. shadingsys = new ShadingSystem (&rend, NULL, &errhandler); // Register the layout of all closures known to this renderer // Any closure used by the shader which is not registered, or // registered with a different number of arguments will lead // to a runtime error. register_closures(shadingsys); // Remember that each shader parameter may optionally have a // metadata hint [[int lockgeom=...]], where 0 indicates that the // parameter may be overridden by the geometry itself, for example // with data interpolated from the mesh vertices, and a value of 1 // means that it is "locked" with respect to the geometry (i.e. it // will not be overridden with interpolated or // per-geometric-primitive data). // // In order to most fully optimize shader, we typically want any // shader parameter not explicitly specified to default to being // locked (i.e. no per-geometry override): shadingsys->attribute("lockgeom", 1); // Now we declare our shader. // // Each material in the scene is comprised of a "shader group." // Each group is comprised of one or more "layers" (a.k.a. shader // instances) with possible connections from outputs of // upstream/early layers into the inputs of downstream/later layers. // A shader instance is the combination of a reference to a shader // master and its parameter values that may override the defaults in // the shader source and may be particular to this instance (versus // all the other instances of the same shader). // // A shader group declaration typically looks like this: // // ShaderGroupRef shadergroup = ss->ShaderGroupBegin (); // ss->Parameter ("paramname", TypeDesc paramtype, void *value); // ... and so on for all the other parameters of... // ss->Shader ("shadertype", "shadername", "layername"); // The Shader() call creates a new instance, which gets // all the pending Parameter() values made right before it. // ... and other shader instances in this group, interspersed with... // ss->ConnectShaders ("layer1", "param1", "layer2", "param2"); // ... and other connections ... // ss->ShaderGroupEnd (); // // It looks so simple, and it really is, except that the way this // testshade program works is that all the Parameter() and Shader() // calls are done inside getargs(), as it walks through the command // line arguments, whereas the connections accumulate and have // to be processed at the end. Bear with us. // Start the shader group and grab a reference to it. shadergroup = shadingsys->ShaderGroupBegin (groupname); // Get the command line arguments. That will set up all the shader // instances and their parameters for the group. getargs (argc, argv); if (! shadergroup) { std::cerr << "ERROR: Invalid shader group. Exiting testshade.\n"; return EXIT_FAILURE; } shadingsys->attribute (shadergroup.get(), "groupname", groupname); // Now set up the connections for (size_t i = 0; i < connections.size(); i += 4) { if (i+3 < connections.size()) { std::cout << "Connect " << connections[i] << "." << connections[i+1] << " to " << connections[i+2] << "." << connections[i+3] << "\n"; shadingsys->ConnectShaders (connections[i].c_str(), connections[i+1].c_str(), connections[i+2].c_str(), connections[i+3].c_str()); } } // End the group shadingsys->ShaderGroupEnd (); if (verbose || do_oslquery) { std::string pickle; shadingsys->getattribute (shadergroup.get(), "pickle", pickle); std::cout << "Shader group:\n---\n" << pickle << "\n---\n"; std::cout << "\n"; ustring groupname; shadingsys->getattribute (shadergroup.get(), "groupname", groupname); std::cout << "Shader group \"" << groupname << "\" layers are:\n"; int num_layers = 0; shadingsys->getattribute (shadergroup.get(), "num_layers", num_layers); if (num_layers > 0) { std::vector<const char *> layers (size_t(num_layers), NULL); shadingsys->getattribute (shadergroup.get(), "layer_names", TypeDesc(TypeDesc::STRING, num_layers), &layers[0]); for (int i = 0; i < num_layers; ++i) { std::cout << " " << (layers[i] ? layers[i] : "<unnamed>") << "\n"; if (do_oslquery) { OSLQuery q; q.init (shadergroup.get(), i); for (size_t p = 0; p < q.nparams(); ++p) { const OSLQuery::Parameter *param = q.getparam(p); std::cout << "\t" << (param->isoutput ? "output " : "") << param->type << ' ' << param->name << "\n"; } } } } std::cout << "\n"; } if (archivegroup.size()) shadingsys->archive_shadergroup (shadergroup.get(), archivegroup); if (outputfiles.size() != 0) std::cout << "\n"; // Set up the named transformations, including shader and object. // For this test application, we just do this statically; in a real // renderer, the global named space (like "myspace") would probably // be static, but shader and object spaces may be different for each // object. setup_transformations (rend, Mshad, Mobj); // Set up the image outputs requested on the command line setup_output_images (shadingsys, shadergroup); if (debug) test_group_attributes (shadergroup.get()); if (num_threads < 1) num_threads = boost::thread::hardware_concurrency(); double setuptime = timer.lap (); // Allow a settable number of iterations to "render" the whole image, // which is useful for time trials of things that would be too quick // to accurately time for a single iteration for (int iter = 0; iter < iters; ++iter) { OIIO::ROI roi (0, xres, 0, yres); if (use_shade_image) OSL::shade_image (*shadingsys, *shadergroup, NULL, *outputimgs[0], outputvarnames, pixelcenters ? ShadePixelCenters : ShadePixelGrid, roi, num_threads); else { bool save = (iter == (iters-1)); // save on last iteration #if 0 shade_region (shadergroup.get(), roi, save); #else OIIO::ImageBufAlgo::parallel_image ( boost::bind (shade_region, shadergroup.get(), _1, save), roi, num_threads); #endif } // If any reparam was requested, do it now if (reparams.size() && reparam_layer.size()) { for (size_t p = 0; p < reparams.size(); ++p) { const ParamValue &pv (reparams[p]); shadingsys->ReParameter (*shadergroup, reparam_layer.c_str(), pv.name().c_str(), pv.type(), pv.data()); } } } double runtime = timer.lap(); if (outputfiles.size() == 0) std::cout << "\n"; // Write the output images to disk for (size_t i = 0; i < outputimgs.size(); ++i) { if (outputimgs[i]) { if (! print_outputs) { std::string filename = outputimgs[i]->name(); // JPEG, GIF, and PNG images should be automatically saved // as sRGB because they are almost certainly supposed to // be displayed on web pages. using namespace OIIO; if (Strutil::iends_with (filename, ".jpg") || Strutil::iends_with (filename, ".jpeg") || Strutil::iends_with (filename, ".gif") || Strutil::iends_with (filename, ".png")) { ImageBuf ccbuf; ImageBufAlgo::colorconvert (ccbuf, *outputimgs[i], "linear", "sRGB", false, "", ""); ccbuf.set_write_format (outputimgs[i]->spec().format); ccbuf.write (filename); } else { outputimgs[i]->write (filename); } } delete outputimgs[i]; outputimgs[i] = NULL; } } // Print some debugging info if (debug || runstats || profile) { double writetime = timer.lap(); std::cout << "\n"; std::cout << "Setup: " << OIIO::Strutil::timeintervalformat (setuptime,2) << "\n"; std::cout << "Run : " << OIIO::Strutil::timeintervalformat (runtime,2) << "\n"; std::cout << "Write: " << OIIO::Strutil::timeintervalformat (writetime,2) << "\n"; std::cout << "\n"; std::cout << shadingsys->getstats (5) << "\n"; OIIO::TextureSystem *texturesys = shadingsys->texturesys(); if (texturesys) std::cout << texturesys->getstats (5) << "\n"; std::cout << ustring::getstats() << "\n"; } // We're done with the shading system now, destroy it shadergroup.reset (); // Must release this before destroying shadingsys delete shadingsys; return EXIT_SUCCESS; }
static void setup_output_images (ShadingSystem *shadingsys, ShaderGroupRef &shadergroup) { // Tell the shading system which outputs we want if (outputvars.size()) { std::vector<const char *> aovnames (outputvars.size()); for (size_t i = 0; i < outputvars.size(); ++i) { ustring varname (outputvars[i]); aovnames[i] = varname.c_str(); size_t dot = varname.find('.'); if (dot != ustring::npos) { // If the name contains a dot, it's intended to be layer.symbol varname = ustring (varname, dot+1); } } shadingsys->attribute (use_group_outputs ? shadergroup.get() : NULL, "renderer_outputs", TypeDesc(TypeDesc::STRING,(int)aovnames.size()), &aovnames[0]); if (use_group_outputs) std::cout << "Marking group outputs, not global renderer outputs.\n"; } if (entrylayers.size()) { std::vector<const char *> layers; std::cout << "Entry layers:"; for (size_t i = 0; i < entrylayers.size(); ++i) { ustring layername (entrylayers[i]); // convert to ustring int layid = shadingsys->find_layer (*shadergroup, layername); layers.push_back (layername.c_str()); entrylayer_index.push_back (layid); std::cout << ' ' << entrylayers[i] << "(" << layid << ")"; } std::cout << "\n"; shadingsys->attribute (shadergroup.get(), "entry_layers", TypeDesc(TypeDesc::STRING,(int)entrylayers.size()), &layers[0]); } if (extraoptions.size()) shadingsys->attribute ("options", extraoptions); ShadingContext *ctx = shadingsys->get_context (); // Because we can only call find_symbol or get_symbol on something that // has been set up to shade (or executed), we call execute() but tell it // not to actually run the shader. ShaderGlobals sg; setup_shaderglobals (sg, shadingsys, 0, 0); if (raytype_opt) shadingsys->optimize_group (shadergroup.get(), raytype_bit, ~raytype_bit); shadingsys->execute (ctx, *shadergroup, sg, false); if (entryoutputs.size()) { std::cout << "Entry outputs:"; for (size_t i = 0; i < entryoutputs.size(); ++i) { ustring name (entryoutputs[i]); // convert to ustring const ShaderSymbol *sym = shadingsys->find_symbol (*shadergroup, name); if (!sym) { std::cout << "\nEntry output " << entryoutputs[i] << " not found. Abording.\n"; exit (EXIT_FAILURE); } entrylayer_symbols.push_back (sym); std::cout << ' ' << entryoutputs[i]; } std::cout << "\n"; } // For each output file specified on the command line... for (size_t i = 0; i < outputfiles.size(); ++i) { // Make a ustring version of the output name, for fast manipulation outputvarnames.push_back (ustring(outputvars[i])); // Start with a NULL ImageBuf pointer outputimgs.push_back (NULL); // Ask for a pointer to the symbol's data, as computed by this // shader. TypeDesc t; const void *data = shadingsys->get_symbol (*ctx, outputvarnames[i], t); if (!data) { std::cout << "Output " << outputvars[i] << " not found, skipping.\n"; continue; // Skip if symbol isn't found } std::cout << "Output " << outputvars[i] << " to " << outputfiles[i] << "\n"; // And the "base" type, i.e. the type of each element or channel TypeDesc tbase = TypeDesc ((TypeDesc::BASETYPE)t.basetype); // But which type are we going to write? Use the true data type // from OSL, unless the command line options indicated that // something else was desired. TypeDesc outtypebase = tbase; if (dataformatname == "uint8") outtypebase = TypeDesc::UINT8; else if (dataformatname == "half") outtypebase = TypeDesc::HALF; else if (dataformatname == "float") outtypebase = TypeDesc::FLOAT; // Number of channels to write to the image is the number of (array) // elements times the number of channels (e.g. 1 for scalar, 3 for // vector, etc.) int nchans = t.numelements() * t.aggregate; // Make an ImageBuf of the right type and size to hold this // symbol's output, and initially clear it to all black pixels. OIIO::ImageSpec spec (xres, yres, nchans, TypeDesc::FLOAT); outputimgs[i] = new OIIO::ImageBuf(outputfiles[i], spec); outputimgs[i]->set_write_format (outtypebase); OIIO::ImageBufAlgo::zero (*outputimgs[i]); } if (outputimgs.empty()) { OIIO::ImageSpec spec (xres, yres, 3, TypeDesc::FLOAT); outputimgs.push_back(new OIIO::ImageBuf(spec)); } shadingsys->release_context (ctx); // don't need this anymore for now }
extern "C" int test_shade (int argc, const char *argv[]) { OIIO::Timer timer; // Create a new shading system. We pass it the RendererServices // object that services callbacks from the shading system, NULL for // the TextureSystem (that just makes 'create' make its own TS), and // an error handler. shadingsys = new ShadingSystem (&rend, NULL, &errhandler); register_closures(shadingsys); // Remember that each shader parameter may optionally have a // metadata hint [[int lockgeom=...]], where 0 indicates that the // parameter may be overridden by the geometry itself, for example // with data interpolated from the mesh vertices, and a value of 1 // means that it is "locked" with respect to the geometry (i.e. it // will not be overridden with interpolated or // per-geometric-primitive data). // // In order to most fully optimize shader, we typically want any // shader parameter not explicitly specified to default to being // locked (i.e. no per-geometry override): shadingsys->attribute("lockgeom", 1); // Now we declare our shader. // // Each material in the scene is comprised of a "shader group." // Each group is comprised of one or more "layers" (a.k.a. shader // instances) with possible connections from outputs of // upstream/early layers into the inputs of downstream/later layers. // A shader instance is the combination of a reference to a shader // master and its parameter values that may override the defaults in // the shader source and may be particular to this instance (versus // all the other instances of the same shader). // // A shader group declaration typically looks like this: // // ShaderGroupRef shadergroup = ss->ShaderGroupBegin (); // ss->Parameter ("paramname", TypeDesc paramtype, void *value); // ... and so on for all the other parameters of... // ss->Shader ("shadertype", "shadername", "layername"); // The Shader() call creates a new instance, which gets // all the pending Parameter() values made right before it. // ... and other shader instances in this group, interspersed with... // ss->ConnectShaders ("layer1", "param1", "layer2", "param2"); // ... and other connections ... // ss->ShaderGroupEnd (); // // It looks so simple, and it really is, except that the way this // testshade program works is that all the Parameter() and Shader() // calls are done inside getargs(), as it walks through the command // line arguments, whereas the connections accumulate and have // to be processed at the end. Bear with us. // Start the shader group and grab a reference to it. ShaderGroupRef shadergroup = shadingsys->ShaderGroupBegin (); // Get the command line arguments. That will set up all the shader // instances and their parameters for the group. getargs (argc, argv); // Now set up the connections for (size_t i = 0; i < connections.size(); i += 4) { if (i+3 < connections.size()) { std::cout << "Connect " << connections[i] << "." << connections[i+1] << " to " << connections[i+2] << "." << connections[i+3] << "\n"; shadingsys->ConnectShaders (connections[i].c_str(), connections[i+1].c_str(), connections[i+2].c_str(), connections[i+3].c_str()); } } // End the group shadingsys->ShaderGroupEnd (); if (outputfiles.size() != 0) std::cout << "\n"; // Set up the named transformations, including shader and object. // For this test application, we just do this statically; in a real // renderer, the global named space (like "myspace") would probably // be static, but shader and object spaces may be different for each // object. setup_transformations (rend, Mshad, Mobj); // Set up the image outputs requested on the command line setup_output_images (shadingsys, shadergroup); if (debug) test_group_attributes (shadergroup.get()); if (num_threads < 1) num_threads = boost::thread::hardware_concurrency(); double setuptime = timer.lap (); // Allow a settable number of iterations to "render" the whole image, // which is useful for time trials of things that would be too quick // to accurately time for a single iteration for (int iter = 0; iter < iters; ++iter) { OIIO::ROI roi (0, xres, 0, yres); bool save = (iter == (iters-1)); // save on last iteration #if 0 shade_region (shadergroup.get(), roi, save); #else OIIO::ImageBufAlgo::parallel_image ( boost::bind (shade_region, shadergroup.get(), _1, save), roi, num_threads); #endif // If any reparam was requested, do it now if (reparams.size() && reparam_layer.size()) { for (size_t p = 0; p < reparams.size(); ++p) { const ParamValue &pv (reparams[p]); shadingsys->ReParameter (*shadergroup, reparam_layer.c_str(), pv.name().c_str(), pv.type(), pv.data()); } } } if (outputfiles.size() == 0) std::cout << "\n"; // Write the output images to disk for (size_t i = 0; i < outputimgs.size(); ++i) { if (outputimgs[i]) { outputimgs[i]->save(); delete outputimgs[i]; outputimgs[i] = NULL; } } // Print some debugging info if (debug || stats) { double runtime = timer.lap(); std::cout << "\n"; std::cout << "Setup: " << OIIO::Strutil::timeintervalformat (setuptime,2) << "\n"; std::cout << "Run : " << OIIO::Strutil::timeintervalformat (runtime,2) << "\n"; std::cout << "\n"; std::cout << shadingsys->getstats (5) << "\n"; OIIO::TextureSystem *texturesys = shadingsys->texturesys(); if (texturesys) std::cout << texturesys->getstats (5) << "\n"; std::cout << ustring::getstats() << "\n"; } // We're done with the shading system now, destroy it shadergroup.reset (); // Must release this before destroying shadingsys delete shadingsys; return EXIT_SUCCESS; }
static void setup_output_images (ShadingSystem *shadingsys, ShaderGroupRef &shadergroup) { // Tell the shading system which outputs we want if (outputvars.size()) { std::vector<const char *> aovnames (outputvars.size()); for (size_t i = 0; i < outputvars.size(); ++i) aovnames[i] = outputvars[i].c_str(); shadingsys->attribute (use_group_outputs ? shadergroup.get() : NULL, "renderer_outputs", TypeDesc(TypeDesc::STRING,(int)aovnames.size()), &aovnames[0]); if (use_group_outputs) std::cout << "Marking group outputs, not global renderer outputs.\n"; } if (extraoptions.size()) shadingsys->attribute ("options", extraoptions); ShadingContext *ctx = shadingsys->get_context (); // Because we can only call get_symbol on something that has been // set up to shade (or executed), we call execute() but tell it not // to actually run the shader. ShaderGlobals sg; setup_shaderglobals (sg, shadingsys, 0, 0); shadingsys->execute (*ctx, *shadergroup, sg, false); // For each output file specified on the command line... for (size_t i = 0; i < outputfiles.size(); ++i) { // Make a ustring version of the output name, for fast manipulation outputvarnames.push_back (ustring(outputvars[i])); // Start with a NULL ImageBuf pointer outputimgs.push_back (NULL); // Ask for a pointer to the symbol's data, as computed by this // shader. TypeDesc t; const void *data = shadingsys->get_symbol (*ctx, outputvarnames[i], t); if (!data) { std::cout << "Output " << outputvars[i] << " not found, skipping.\n"; continue; // Skip if symbol isn't found } std::cout << "Output " << outputvars[i] << " to " << outputfiles[i] << "\n"; // And the "base" type, i.e. the type of each element or channel TypeDesc tbase = TypeDesc ((TypeDesc::BASETYPE)t.basetype); // But which type are we going to write? Use the true data type // from OSL, unless the command line options indicated that // something else was desired. TypeDesc outtypebase = tbase; if (dataformatname == "uint8") outtypebase = TypeDesc::UINT8; else if (dataformatname == "half") outtypebase = TypeDesc::HALF; else if (dataformatname == "float") outtypebase = TypeDesc::FLOAT; // Number of channels to write to the image is the number of (array) // elements times the number of channels (e.g. 1 for scalar, 3 for // vector, etc.) int nchans = t.numelements() * t.aggregate; // Make an ImageBuf of the right type and size to hold this // symbol's output, and initially clear it to all black pixels. OIIO::ImageSpec spec (xres, yres, nchans, outtypebase); outputimgs[i] = new OIIO::ImageBuf(outputfiles[i], spec); OIIO::ImageBufAlgo::zero (*outputimgs[i]); } shadingsys->release_context (ctx); // don't need this anymore for now }