Cycles: add option to bake specular from active camera viewpoint

Previously it would bake viewed from above the surface. The new option can be
useful when the baked result is meant to be viewed from a fixed viewpoint or
with limited camera motion.

Some effort is made to give a continuous reflection on parts of the surface
invisible to the camera, but this is necessarily only a rough approximation.

Differential Revision: https://developer.blender.org/D15921
This commit is contained in:
Phoenix Katsch 2022-10-03 19:26:02 +02:00 committed by Brecht Van Lommel
parent af51e4b41c
commit b475506cfb
8 changed files with 99 additions and 4 deletions

View File

@ -1880,6 +1880,12 @@ class CYCLES_RENDER_PT_bake(CyclesButtonsPanel, Panel):
layout.prop(rd, "use_bake_multires")
layout.prop(cscene, "bake_type")
if not rd.use_bake_multires and cscene.bake_type not in {
"AO", "POSITION", "NORMAL", "UV", "ROUGHNESS", "ENVIRONMENT"}:
row = layout.row()
row.prop(cbk, "view_from")
row.active = scene.camera is not None
class CYCLES_RENDER_PT_bake_influence(CyclesButtonsPanel, Panel):
bl_label = "Influence"

View File

@ -2,6 +2,7 @@
* Copyright 2011-2022 Blender Foundation */
#include "scene/camera.h"
#include "scene/bake.h"
#include "scene/scene.h"
#include "blender/sync.h"
@ -592,6 +593,11 @@ void BlenderSync::sync_camera(BL::RenderSettings &b_render,
blender_camera_from_object(&bcam, b_engine, b_ob);
b_engine.camera_model_matrix(b_ob, bcam.use_spherical_stereo, b_ob_matrix);
bcam.matrix = get_transform(b_ob_matrix);
scene->bake_manager->set_use_camera(b_render.bake().view_from() ==
BL::BakeSettings::view_from_ACTIVE_CAMERA);
}
else {
scene->bake_manager->set_use_camera(false);
}
/* sync */

View File

@ -210,8 +210,51 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
/* Setup ray. */
Ray ray ccl_optional_struct_init;
ray.P = P + N;
ray.D = -N;
if (kernel_data.bake.use_camera) {
float3 D = camera_direction_from_point(kg, P);
const float DN = dot(D, N);
/* Nudge camera direction, so that the faces facing away from the camera still have
* somewhat usable shading. (Otherwise, glossy faces would be simply black.)
*
* The surface normal offset affects smooth surfaces. Lower values will make
* smooth surfaces more faceted, but higher values may show up from the camera
* at grazing angles.
*
* This value can actually be pretty high before it's noticeably wrong. */
const float surface_normal_offset = 0.2f;
/* Keep the ray direction at least `surface_normal_offset` "above" the smooth normal. */
if (DN <= surface_normal_offset) {
D -= N * (DN - surface_normal_offset);
D = normalize(D);
}
/* On the backside, just lerp towards the surface normal for the ray direction,
* as DN goes from 0.0 to -1.0. */
if (DN <= 0.0f) {
D = normalize(mix(D, N, -DN));
}
/* We don't want to bake the back face, so make sure the ray direction never
* goes behind the geometry (flat) normal. This is a failsafe, and should rarely happen. */
const float true_normal_epsilon = 0.00001f;
if (dot(D, Ng) <= true_normal_epsilon) {
D -= Ng * (dot(D, Ng) - true_normal_epsilon);
D = normalize(D);
}
ray.P = P + D;
ray.D = -D;
}
else {
ray.P = P + N;
ray.D = -N;
}
ray.tmin = 0.0f;
ray.tmax = FLT_MAX;
ray.time = 0.5f;

View File

@ -1160,7 +1160,7 @@ typedef struct KernelBake {
int use;
int object_index;
int tri_offset;
int pad1;
int use_camera;
} KernelBake;
static_assert_align(KernelBake, 16);

View File

@ -16,6 +16,7 @@ CCL_NAMESPACE_BEGIN
BakeManager::BakeManager()
{
need_update_ = true;
use_camera_ = false;
}
BakeManager::~BakeManager()
@ -38,6 +39,14 @@ void BakeManager::set(Scene *scene, const std::string &object_name_)
need_update_ = true;
}
void BakeManager::set_use_camera(const bool use_camera)
{
if (use_camera_ != use_camera) {
use_camera_ = use_camera;
need_update_ = true;
}
}
void BakeManager::device_update(Device * /*device*/,
DeviceScene *dscene,
Scene *scene,
@ -49,6 +58,8 @@ void BakeManager::device_update(Device * /*device*/,
KernelBake *kbake = &dscene->data.bake;
memset(kbake, 0, sizeof(*kbake));
kbake->use_camera = use_camera_;
if (!object_name.empty()) {
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {

View File

@ -20,6 +20,8 @@ class BakeManager {
void set(Scene *scene, const std::string &object_name);
bool get_baking() const;
void set_use_camera(bool use_camera);
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
void device_free(Device *device, DeviceScene *dscene);
@ -30,6 +32,7 @@ class BakeManager {
private:
bool need_update_;
std::string object_name;
bool use_camera_;
};
CCL_NAMESPACE_END

View File

@ -559,7 +559,8 @@ typedef struct BakeData {
char target;
char save_mode;
char margin_type;
char _pad[5];
char view_from;
char _pad[4];
struct Object *cage_object;
} BakeData;
@ -592,6 +593,12 @@ typedef enum eBakeSaveMode {
R_BAKE_SAVE_EXTERNAL = 1,
} eBakeSaveMode;
/** #BakeData.view_from (char) */
typedef enum eBakeViewFrom {
R_BAKE_VIEW_FROM_ABOVE_SURFACE = 0,
R_BAKE_VIEW_FROM_ACTIVE_CAMERA = 1,
} eBakeViewFrom;
/** #BakeData.pass_filter */
typedef enum eBakePassFilter {
R_BAKE_PASS_FILTER_NONE = 0,

View File

@ -476,6 +476,20 @@ const EnumPropertyItem rna_enum_bake_save_mode_items[] = {
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_bake_view_from_items[] = {
{R_BAKE_VIEW_FROM_ABOVE_SURFACE,
"ABOVE_SURFACE",
0,
"Above Surface",
"Cast rays from above the surface"},
{R_BAKE_VIEW_FROM_ACTIVE_CAMERA,
"ACTIVE_CAMERA",
0,
"Active Camera",
"Use the active camera's position to cast rays"},
{0, NULL, 0, NULL, NULL},
};
#define R_IMF_VIEWS_ENUM_IND \
{R_IMF_VIEWS_INDIVIDUAL, \
"INDIVIDUAL", \
@ -5413,6 +5427,11 @@ static void rna_def_bake_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Save Mode", "Where to save baked image textures");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "view_from", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_bake_view_from_items);
RNA_def_property_ui_text(prop, "View From", "Source of reflection ray directions");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
/* flags */
prop = RNA_def_property(srna, "use_selected_to_active", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", R_BAKE_TO_ACTIVE);