
Volumes can now have textured colors and density. There is a Volume Sampling panel in the Render properties with these settings: * Step size: distance between volume shader samples when rendering the volume. Lower values give more accurate and detailed results but also increased render time. * Max steps: maximum number of steps through the volume before giving up, to protect from extremely long render times with big objects or small step sizes. This is much more compute intensive than homogeneous volume, so when you are not using a texture you should enable the Homogeneous Volume option in the material or world for faster rendering. One important missing feature is that Generated texture coordinates are not yet working in volumes, and they are the default coordinates for nearly all texture nodes. So until that works you need to plug in object texture coordinates or a world space position. This is work by "storm", Stuart Broadfoot, Thomas Dinges and myself.
360 lines
9.1 KiB
C++
360 lines
9.1 KiB
C++
/*
|
|
* Copyright 2011-2013 Blender Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License
|
|
*/
|
|
|
|
#include "background.h"
|
|
#include "blackbody.h"
|
|
#include "device.h"
|
|
#include "graph.h"
|
|
#include "light.h"
|
|
#include "mesh.h"
|
|
#include "nodes.h"
|
|
#include "osl.h"
|
|
#include "scene.h"
|
|
#include "shader.h"
|
|
#include "svm.h"
|
|
#include "tables.h"
|
|
|
|
#include "util_foreach.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
/* Shader */
|
|
|
|
Shader::Shader()
|
|
{
|
|
name = "";
|
|
pass_id = 0;
|
|
|
|
graph = NULL;
|
|
graph_bump = NULL;
|
|
|
|
use_mis = true;
|
|
use_transparent_shadow = true;
|
|
heterogeneous_volume = true;
|
|
|
|
has_surface = false;
|
|
has_surface_transparent = false;
|
|
has_surface_emission = false;
|
|
has_surface_bssrdf = false;
|
|
has_converter_blackbody = false;
|
|
has_volume = false;
|
|
has_displacement = false;
|
|
has_bssrdf_bump = false;
|
|
|
|
used = false;
|
|
|
|
need_update = true;
|
|
need_update_attributes = true;
|
|
}
|
|
|
|
Shader::~Shader()
|
|
{
|
|
delete graph;
|
|
delete graph_bump;
|
|
}
|
|
|
|
void Shader::set_graph(ShaderGraph *graph_)
|
|
{
|
|
/* do this here already so that we can detect if mesh or object attributes
|
|
* are needed, since the node attribute callbacks check if their sockets
|
|
* are connected but proxy nodes should not count */
|
|
if(graph_)
|
|
graph_->remove_unneeded_nodes();
|
|
|
|
/* assign graph */
|
|
delete graph;
|
|
delete graph_bump;
|
|
graph = graph_;
|
|
graph_bump = NULL;
|
|
}
|
|
|
|
void Shader::tag_update(Scene *scene)
|
|
{
|
|
/* update tag */
|
|
need_update = true;
|
|
scene->shader_manager->need_update = true;
|
|
|
|
/* if the shader previously was emissive, update light distribution,
|
|
* if the new shader is emissive, a light manager update tag will be
|
|
* done in the shader manager device update. */
|
|
if(use_mis && has_surface_emission)
|
|
scene->light_manager->need_update = true;
|
|
|
|
/* get requested attributes. this could be optimized by pruning unused
|
|
* nodes here already, but that's the job of the shader manager currently,
|
|
* and may not be so great for interactive rendering where you temporarily
|
|
* disconnect a node */
|
|
AttributeRequestSet prev_attributes = attributes;
|
|
|
|
attributes.clear();
|
|
foreach(ShaderNode *node, graph->nodes)
|
|
node->attributes(&attributes);
|
|
|
|
/* compare if the attributes changed, mesh manager will check
|
|
* need_update_attributes, update the relevant meshes and clear it. */
|
|
if(attributes.modified(prev_attributes)) {
|
|
need_update_attributes = true;
|
|
scene->mesh_manager->need_update = true;
|
|
}
|
|
}
|
|
|
|
void Shader::tag_used(Scene *scene)
|
|
{
|
|
/* if an unused shader suddenly gets used somewhere, it needs to be
|
|
* recompiled because it was skipped for compilation before */
|
|
if(!used) {
|
|
need_update = true;
|
|
scene->shader_manager->need_update = true;
|
|
}
|
|
}
|
|
|
|
/* Shader Manager */
|
|
|
|
ShaderManager::ShaderManager()
|
|
{
|
|
need_update = true;
|
|
blackbody_table_offset = TABLE_OFFSET_INVALID;
|
|
}
|
|
|
|
ShaderManager::~ShaderManager()
|
|
{
|
|
}
|
|
|
|
ShaderManager *ShaderManager::create(Scene *scene, int shadingsystem)
|
|
{
|
|
ShaderManager *manager;
|
|
|
|
#ifdef WITH_OSL
|
|
if(shadingsystem == SceneParams::OSL)
|
|
manager = new OSLShaderManager();
|
|
else
|
|
#endif
|
|
manager = new SVMShaderManager();
|
|
|
|
add_default(scene);
|
|
|
|
return manager;
|
|
}
|
|
|
|
uint ShaderManager::get_attribute_id(ustring name)
|
|
{
|
|
/* get a unique id for each name, for SVM attribute lookup */
|
|
AttributeIDMap::iterator it = unique_attribute_id.find(name);
|
|
|
|
if(it != unique_attribute_id.end())
|
|
return it->second;
|
|
|
|
uint id = (uint)ATTR_STD_NUM + unique_attribute_id.size();
|
|
unique_attribute_id[name] = id;
|
|
return id;
|
|
}
|
|
|
|
uint ShaderManager::get_attribute_id(AttributeStandard std)
|
|
{
|
|
return (uint)std;
|
|
}
|
|
|
|
int ShaderManager::get_shader_id(uint shader, Mesh *mesh, bool smooth)
|
|
{
|
|
/* get a shader id to pass to the kernel */
|
|
int id = shader*2;
|
|
|
|
/* index depends bump since this setting is not in the shader */
|
|
if(mesh && mesh->displacement_method != Mesh::DISPLACE_TRUE)
|
|
id += 1;
|
|
/* smooth flag */
|
|
if(smooth)
|
|
id |= SHADER_SMOOTH_NORMAL;
|
|
|
|
/* default flags */
|
|
id |= SHADER_CAST_SHADOW|SHADER_AREA_LIGHT;
|
|
|
|
return id;
|
|
}
|
|
|
|
void ShaderManager::device_update_shaders_used(Scene *scene)
|
|
{
|
|
/* figure out which shaders are in use, so SVM/OSL can skip compiling them
|
|
* for speed and avoid loading image textures into memory */
|
|
foreach(Shader *shader, scene->shaders)
|
|
shader->used = false;
|
|
|
|
scene->shaders[scene->background->shader]->used = true;
|
|
scene->shaders[scene->default_surface]->used = true;
|
|
scene->shaders[scene->default_light]->used = true;
|
|
scene->shaders[scene->default_background]->used = true;
|
|
scene->shaders[scene->default_empty]->used = true;
|
|
|
|
foreach(Mesh *mesh, scene->meshes)
|
|
foreach(uint shader, mesh->used_shaders)
|
|
scene->shaders[shader]->used = true;
|
|
|
|
foreach(Light *light, scene->lights)
|
|
scene->shaders[light->shader]->used = true;
|
|
}
|
|
|
|
void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
|
|
{
|
|
device->tex_free(dscene->shader_flag);
|
|
dscene->shader_flag.clear();
|
|
|
|
if(scene->shaders.size() == 0)
|
|
return;
|
|
|
|
uint shader_flag_size = scene->shaders.size()*4;
|
|
uint *shader_flag = dscene->shader_flag.resize(shader_flag_size);
|
|
uint i = 0;
|
|
bool has_converter_blackbody = false;
|
|
bool has_volumes = false;
|
|
|
|
foreach(Shader *shader, scene->shaders) {
|
|
uint flag = 0;
|
|
|
|
if(shader->use_mis)
|
|
flag |= SD_USE_MIS;
|
|
if(shader->has_surface_transparent && shader->use_transparent_shadow)
|
|
flag |= SD_HAS_TRANSPARENT_SHADOW;
|
|
if(shader->has_volume) {
|
|
flag |= SD_HAS_VOLUME;
|
|
has_volumes = true;
|
|
|
|
/* in this case we can assume transparent surface */
|
|
if(!shader->has_surface)
|
|
flag |= SD_HAS_ONLY_VOLUME;
|
|
|
|
/* todo: this could check more fine grained, to skip useless volumes
|
|
* enclosed inside an opaque bsdf, although we still need to handle
|
|
* the case with camera inside volumes too */
|
|
flag |= SD_HAS_TRANSPARENT_SHADOW;
|
|
}
|
|
if(shader->heterogeneous_volume)
|
|
flag |= SD_HETEROGENEOUS_VOLUME;
|
|
if(shader->has_bssrdf_bump)
|
|
flag |= SD_HAS_BSSRDF_BUMP;
|
|
if(shader->has_converter_blackbody)
|
|
has_converter_blackbody = true;
|
|
|
|
/* regular shader */
|
|
shader_flag[i++] = flag;
|
|
shader_flag[i++] = shader->pass_id;
|
|
|
|
/* shader with bump mapping */
|
|
if(shader->graph_bump)
|
|
flag |= SD_HAS_BSSRDF_BUMP;
|
|
|
|
shader_flag[i++] = flag;
|
|
shader_flag[i++] = shader->pass_id;
|
|
}
|
|
|
|
device->tex_alloc("__shader_flag", dscene->shader_flag);
|
|
|
|
/* blackbody lookup table */
|
|
KernelBlackbody *kblackbody = &dscene->data.blackbody;
|
|
|
|
if(has_converter_blackbody && blackbody_table_offset == TABLE_OFFSET_INVALID) {
|
|
vector<float> table = blackbody_table();
|
|
blackbody_table_offset = scene->lookup_tables->add_table(dscene, table);
|
|
|
|
kblackbody->table_offset = (int)blackbody_table_offset;
|
|
}
|
|
else if(!has_converter_blackbody && blackbody_table_offset != TABLE_OFFSET_INVALID) {
|
|
scene->lookup_tables->remove_table(blackbody_table_offset);
|
|
blackbody_table_offset = TABLE_OFFSET_INVALID;
|
|
}
|
|
|
|
/* volumes */
|
|
KernelIntegrator *kintegrator = &dscene->data.integrator;
|
|
kintegrator->use_volumes = has_volumes;
|
|
}
|
|
|
|
void ShaderManager::device_free_common(Device *device, DeviceScene *dscene, Scene *scene)
|
|
{
|
|
if(blackbody_table_offset != TABLE_OFFSET_INVALID) {
|
|
scene->lookup_tables->remove_table(blackbody_table_offset);
|
|
blackbody_table_offset = TABLE_OFFSET_INVALID;
|
|
}
|
|
|
|
device->tex_free(dscene->shader_flag);
|
|
dscene->shader_flag.clear();
|
|
}
|
|
|
|
void ShaderManager::add_default(Scene *scene)
|
|
{
|
|
Shader *shader;
|
|
ShaderGraph *graph;
|
|
ShaderNode *closure, *out;
|
|
|
|
/* default surface */
|
|
{
|
|
graph = new ShaderGraph();
|
|
|
|
closure = graph->add(new DiffuseBsdfNode());
|
|
closure->input("Color")->value = make_float3(0.8f, 0.8f, 0.8f);
|
|
out = graph->output();
|
|
|
|
graph->connect(closure->output("BSDF"), out->input("Surface"));
|
|
|
|
shader = new Shader();
|
|
shader->name = "default_surface";
|
|
shader->graph = graph;
|
|
scene->shaders.push_back(shader);
|
|
scene->default_surface = scene->shaders.size() - 1;
|
|
}
|
|
|
|
/* default light */
|
|
{
|
|
graph = new ShaderGraph();
|
|
|
|
closure = graph->add(new EmissionNode());
|
|
closure->input("Color")->value = make_float3(0.8f, 0.8f, 0.8f);
|
|
closure->input("Strength")->value.x = 0.0f;
|
|
out = graph->output();
|
|
|
|
graph->connect(closure->output("Emission"), out->input("Surface"));
|
|
|
|
shader = new Shader();
|
|
shader->name = "default_light";
|
|
shader->graph = graph;
|
|
scene->shaders.push_back(shader);
|
|
scene->default_light = scene->shaders.size() - 1;
|
|
}
|
|
|
|
/* default background */
|
|
{
|
|
graph = new ShaderGraph();
|
|
|
|
shader = new Shader();
|
|
shader->name = "default_background";
|
|
shader->graph = graph;
|
|
scene->shaders.push_back(shader);
|
|
scene->default_background = scene->shaders.size() - 1;
|
|
}
|
|
|
|
/* default empty */
|
|
{
|
|
graph = new ShaderGraph();
|
|
|
|
shader = new Shader();
|
|
shader->name = "default_empty";
|
|
shader->graph = graph;
|
|
scene->shaders.push_back(shader);
|
|
scene->default_empty = scene->shaders.size() - 1;
|
|
}
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|
|
|