mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Cycles: Use per-corner normals instead of splitting
This is more consistent with Blender and fixes various discrepancies between Cycles and Blender with sharp edges and mixed flat and smooth faces. This uses more memory, but is compensated by earlier octahedral mapping and in the benchmark scenes memory is always the same or lower. Fix #152812: Mix of flat and smooth faces has incorrect sharp edges Fix #152496: Smooth faces that share one vertex have bad normals Fix #100425: Issues with motion blur and autosmooth or edge splitting Pull Request: https://projects.blender.org/blender/blender/pulls/153836
This commit is contained in:
parent
9b8810a351
commit
6bbdad08fd
24 changed files with 307 additions and 165 deletions
|
|
@ -34,45 +34,11 @@
|
|||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "GEO_mesh_split_edges.hh"
|
||||
#include "util/types_normal.h"
|
||||
|
||||
using blender::Attribute;
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
void mesh_split_edges_for_corner_normals(blender::Mesh &mesh)
|
||||
{
|
||||
using namespace blender;
|
||||
const OffsetIndices polys = mesh.faces();
|
||||
const Span<int> corner_edges = mesh.corner_edges();
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
const VArray<bool> mesh_sharp_edges = *attributes.lookup_or_default<bool>(
|
||||
"sharp_edge", bke::AttrDomain::Edge, false);
|
||||
const VArraySpan<bool> sharp_faces = *attributes.lookup<bool>("sharp_face",
|
||||
bke::AttrDomain::Face);
|
||||
|
||||
Array<bool> sharp_edges(mesh.edges_num);
|
||||
mesh_sharp_edges.materialize(sharp_edges);
|
||||
|
||||
threading::parallel_for(polys.index_range(), 1024, [&](const blender::IndexRange range) {
|
||||
for (const int face_i : range) {
|
||||
if (!sharp_faces.is_empty() && sharp_faces[face_i]) {
|
||||
for (const int edge : corner_edges.slice(polys[face_i])) {
|
||||
sharp_edges[edge] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask split_mask = IndexMask::from_bools(sharp_edges, memory);
|
||||
if (split_mask.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
geometry::split_edges(mesh, split_mask, {});
|
||||
}
|
||||
|
||||
static void attr_create_motion_from_velocity(Mesh *mesh,
|
||||
const blender::Span<blender::float3> b_attr,
|
||||
const float motion_scale)
|
||||
|
|
@ -641,10 +607,6 @@ static void create_mesh(Scene *scene,
|
|||
"material_index", blender::bke::AttrDomain::Face);
|
||||
const blender::VArraySpan sharp_faces = *b_attributes.lookup<bool>(
|
||||
"sharp_face", blender::bke::AttrDomain::Face);
|
||||
blender::Span<blender::float3> corner_normals;
|
||||
if (use_corner_normals) {
|
||||
corner_normals = b_mesh.corner_normals();
|
||||
}
|
||||
|
||||
int numtris = 0;
|
||||
if (!subdivision) {
|
||||
|
|
@ -662,13 +624,14 @@ static void create_mesh(Scene *scene,
|
|||
}
|
||||
|
||||
AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
|
||||
Attribute *attr_N = attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
packed_normal *N = attr_N->data_normal();
|
||||
|
||||
if (subdivision || !(use_corner_normals && !corner_normals.is_empty())) {
|
||||
if (subdivision || !use_corner_normals) {
|
||||
Attribute *attr_N = attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
packed_normal *N = attr_N->data_normal();
|
||||
const blender::Span<blender::float3> vert_normals = b_mesh.vert_normals();
|
||||
for (const int i : vert_normals.index_range()) {
|
||||
N[i] = make_float3(vert_normals[i][0], vert_normals[i][1], vert_normals[i][2]);
|
||||
N[i] = packed_normal(
|
||||
make_float3(vert_normals[i][0], vert_normals[i][1], vert_normals[i][2]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -717,9 +680,9 @@ static void create_mesh(Scene *scene,
|
|||
bool *smooth = mesh->get_smooth().data();
|
||||
int *shader = mesh->get_shader().data();
|
||||
|
||||
const blender::Span<blender::int3> corner_tris = b_mesh.corner_tris();
|
||||
for (const int i : corner_tris.index_range()) {
|
||||
const blender::int3 &tri = corner_tris[i];
|
||||
const blender::Span<blender::int3> b_corner_tris = b_mesh.corner_tris();
|
||||
for (const int i : b_corner_tris.index_range()) {
|
||||
const blender::int3 &tri = b_corner_tris[i];
|
||||
triangles[i * 3 + 0] = corner_verts[tri[0]];
|
||||
triangles[i * 3 + 1] = corner_verts[tri[1]];
|
||||
triangles[i * 3 + 2] = corner_verts[tri[2]];
|
||||
|
|
@ -737,7 +700,7 @@ static void create_mesh(Scene *scene,
|
|||
std::fill(shader, shader + numtris, 0);
|
||||
}
|
||||
|
||||
if (!sharp_faces.is_empty() && !(use_corner_normals && !corner_normals.is_empty())) {
|
||||
if (!sharp_faces.is_empty()) {
|
||||
for (const int face : faces.index_range()) {
|
||||
const bool face_smooth = !sharp_faces[face];
|
||||
const blender::IndexRange face_tris = blender::bke::mesh::face_triangles_range(faces,
|
||||
|
|
@ -750,14 +713,17 @@ static void create_mesh(Scene *scene,
|
|||
std::fill(smooth, smooth + numtris, normals_domain != blender::bke::MeshNormalDomain::Face);
|
||||
}
|
||||
|
||||
if (use_corner_normals && !corner_normals.is_empty()) {
|
||||
for (const int i : corner_tris.index_range()) {
|
||||
const blender::int3 &tri = corner_tris[i];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const int corner = tri[i];
|
||||
const int vert = corner_verts[corner];
|
||||
const float *normal = corner_normals[corner];
|
||||
N[vert] = packed_normal(make_float3(normal[0], normal[1], normal[2]));
|
||||
if (use_corner_normals) {
|
||||
const blender::Span<blender::float3> b_corner_normals = b_mesh.corner_normals();
|
||||
Attribute *attr_N = attributes.add(ATTR_STD_CORNER_NORMAL);
|
||||
packed_normal *N = attr_N->data_normal();
|
||||
|
||||
for (const int i : b_corner_tris.index_range()) {
|
||||
const blender::int3 &tri = b_corner_tris[i];
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const int corner = tri[j];
|
||||
const float *normal = b_corner_normals[corner];
|
||||
N[i * 3 + j] = packed_normal(make_float3(normal[0], normal[1], normal[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -999,6 +965,7 @@ void BlenderSync::sync_mesh_motion(BObjectInfo &b_ob_info, Mesh *mesh, const int
|
|||
{
|
||||
/* Skip if no vertices were exported. */
|
||||
const size_t numverts = mesh->get_verts().size();
|
||||
const size_t numtris = mesh->num_triangles();
|
||||
if (numverts == 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1030,7 +997,9 @@ void BlenderSync::sync_mesh_motion(BObjectInfo &b_ob_info, Mesh *mesh, const int
|
|||
/* Find attributes. */
|
||||
Attribute *attr_mP = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
Attribute *attr_mN = attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
|
||||
Attribute *attr_mcN = mesh->attributes.find(ATTR_STD_MOTION_CORNER_NORMAL);
|
||||
Attribute *attr_N = attributes.find(ATTR_STD_VERTEX_NORMAL);
|
||||
Attribute *attr_cN = mesh->attributes.find(ATTR_STD_CORNER_NORMAL);
|
||||
bool new_attribute = false;
|
||||
/* Add new attributes if they don't exist already. */
|
||||
if (!attr_mP) {
|
||||
|
|
@ -1038,12 +1007,19 @@ void BlenderSync::sync_mesh_motion(BObjectInfo &b_ob_info, Mesh *mesh, const int
|
|||
if (attr_N) {
|
||||
attr_mN = attributes.add(ATTR_STD_MOTION_VERTEX_NORMAL);
|
||||
}
|
||||
if (attr_cN) {
|
||||
attr_mcN = mesh->attributes.add(ATTR_STD_MOTION_CORNER_NORMAL);
|
||||
}
|
||||
|
||||
new_attribute = true;
|
||||
}
|
||||
/* Load vertex data from mesh. */
|
||||
float3 *mP = attr_mP->data_float3() + motion_step * numverts;
|
||||
packed_normal *mN = (attr_mN) ? attr_mN->data_normal() + motion_step * numverts : nullptr;
|
||||
packed_normal *mcN = (attr_mcN) ? attr_mcN->data_normal() + motion_step * numtris * 3 :
|
||||
nullptr;
|
||||
|
||||
bool topology_changed = b_verts_num != numverts;
|
||||
|
||||
/* NOTE: We don't copy more that existing amount of vertices to prevent
|
||||
* possible memory corruption.
|
||||
|
|
@ -1058,13 +1034,28 @@ void BlenderSync::sync_mesh_motion(BObjectInfo &b_ob_info, Mesh *mesh, const int
|
|||
make_float3(b_vert_normals[i][0], b_vert_normals[i][1], b_vert_normals[i][2]));
|
||||
}
|
||||
}
|
||||
if (mcN) {
|
||||
const blender::Span<blender::float3> b_corner_normals = b_mesh->corner_normals();
|
||||
const blender::Span<blender::int3> b_corner_tris = b_mesh->corner_tris();
|
||||
const int mincorners = std::min<int>(b_corner_tris.size(), numtris);
|
||||
|
||||
for (int i = 0; i < mincorners; i++) {
|
||||
const blender::int3 &tri = b_corner_tris[i];
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const int corner = tri[j];
|
||||
const float *normal = b_corner_normals[corner];
|
||||
mcN[i * 3 + j] = packed_normal(make_float3(normal[0], normal[1], normal[2]));
|
||||
}
|
||||
}
|
||||
|
||||
topology_changed |= b_corner_tris.size() != numtris;
|
||||
}
|
||||
if (new_attribute) {
|
||||
/* In case of new attribute, we verify if there really was any motion. */
|
||||
if (b_verts_num != numverts ||
|
||||
memcmp(mP, mesh->get_verts().data(), sizeof(float3) * numverts) == 0)
|
||||
if (topology_changed || memcmp(mP, mesh->get_verts().data(), sizeof(float3) * numverts) == 0)
|
||||
{
|
||||
/* no motion, remove attributes again */
|
||||
if (b_verts_num != numverts) {
|
||||
if (topology_changed) {
|
||||
LOG_WARNING << "Topology differs, disabling motion blur for object " << ob_name;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1074,6 +1065,9 @@ void BlenderSync::sync_mesh_motion(BObjectInfo &b_ob_info, Mesh *mesh, const int
|
|||
if (attr_mN) {
|
||||
attributes.remove(ATTR_STD_MOTION_VERTEX_NORMAL);
|
||||
}
|
||||
if (attr_mcN) {
|
||||
attributes.remove(ATTR_STD_MOTION_CORNER_NORMAL);
|
||||
}
|
||||
}
|
||||
else if (motion_step > 0) {
|
||||
LOG_TRACE << "Filling deformation motion for object " << ob_name;
|
||||
|
|
@ -1081,24 +1075,32 @@ void BlenderSync::sync_mesh_motion(BObjectInfo &b_ob_info, Mesh *mesh, const int
|
|||
* they had no motion, but we need them anyway now */
|
||||
const float3 *P = mesh->get_verts().data();
|
||||
const packed_normal *N = (attr_N) ? attr_N->data_normal() : nullptr;
|
||||
const packed_normal *cN = (attr_cN) ? attr_cN->data_normal() : nullptr;
|
||||
for (int step = 0; step < motion_step; step++) {
|
||||
std::copy_n(P, numverts, attr_mP->data_float3() + step * numverts);
|
||||
if (attr_mN) {
|
||||
std::copy_n(N, numverts, attr_mN->data_normal() + step * numverts);
|
||||
}
|
||||
if (attr_mcN) {
|
||||
std::copy_n(cN, numtris * 3, attr_mcN->data_normal() + step * (numtris * 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (b_verts_num != numverts) {
|
||||
if (topology_changed) {
|
||||
LOG_WARNING << "Topology differs, discarding motion blur for object " << ob_name
|
||||
<< " at time " << motion_step;
|
||||
const float3 *P = mesh->get_verts().data();
|
||||
const packed_normal *N = (attr_N) ? attr_N->data_normal() : nullptr;
|
||||
const packed_normal *cN = (attr_cN) ? attr_cN->data_normal() : nullptr;
|
||||
std::copy_n(P, numverts, mP);
|
||||
if (mN != nullptr) {
|
||||
std::copy_n(N, numverts, mN);
|
||||
}
|
||||
if (mcN != nullptr) {
|
||||
std::copy_n(cN, numtris * 3, mcN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,50 +89,28 @@ int blender_attribute_name_split_type(ustring name, string *r_real_name);
|
|||
void python_thread_state_save(void **python_thread_state);
|
||||
void python_thread_state_restore(void **python_thread_state);
|
||||
|
||||
static bool mesh_use_corner_normals(const BObjectInfo &b_ob_info, blender::Mesh *mesh)
|
||||
{
|
||||
return mesh && !b_ob_info.use_adaptive_subdivision &&
|
||||
(mesh->normals_domain(true) == blender::bke::MeshNormalDomain::Corner);
|
||||
}
|
||||
|
||||
void mesh_split_edges_for_corner_normals(blender::Mesh &mesh);
|
||||
|
||||
static inline blender::Mesh *object_to_mesh(BObjectInfo &b_ob_info)
|
||||
{
|
||||
blender::Mesh *mesh = (GS(b_ob_info.object_data->name) == blender::ID_ME) ?
|
||||
blender::id_cast<blender::Mesh *>(b_ob_info.object_data) :
|
||||
nullptr;
|
||||
|
||||
bool use_corner_normals = false;
|
||||
|
||||
if (b_ob_info.is_real_object_data()) {
|
||||
if (mesh) {
|
||||
if (mesh->runtime->edit_mesh) {
|
||||
/* Flush edit-mesh to mesh, including all data layers. */
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
use_corner_normals = mesh_use_corner_normals(b_ob_info, mesh);
|
||||
}
|
||||
else if (mesh_use_corner_normals(b_ob_info, mesh)) {
|
||||
/* Make a copy to split faces. */
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
use_corner_normals = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
use_corner_normals = mesh_use_corner_normals(b_ob_info, mesh);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* TODO: what to do about non-mesh geometry instances? */
|
||||
use_corner_normals = mesh_use_corner_normals(b_ob_info, mesh);
|
||||
}
|
||||
|
||||
if (mesh) {
|
||||
if (use_corner_normals) {
|
||||
mesh_split_edges_for_corner_normals(*mesh);
|
||||
}
|
||||
|
||||
if (b_ob_info.use_adaptive_subdivision) {
|
||||
mesh->corner_tris();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,12 +44,6 @@ ccl_device_inline T curve_attribute_dfdy(const ccl_private differential &du,
|
|||
return du.dy * (f1 - f0);
|
||||
}
|
||||
|
||||
# ifdef __KERNEL_METAL__
|
||||
template<typename U, typename V> using curve_attribute_is_same = metal::is_same<U, V>;
|
||||
# else
|
||||
template<typename U, typename V> using curve_attribute_is_same = std::is_same<U, V>;
|
||||
# endif
|
||||
|
||||
/* Read attributes on various curve elements, and compute the partial derivatives if requested. */
|
||||
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -54,9 +54,10 @@ ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals kg,
|
|||
|
||||
ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals kg,
|
||||
const int object,
|
||||
const int object_flag,
|
||||
const int prim,
|
||||
const uint3 tri_vindex,
|
||||
int offset,
|
||||
const int numverts,
|
||||
const int numsteps,
|
||||
int step,
|
||||
float3 normals[3])
|
||||
|
|
@ -70,12 +71,30 @@ ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals kg,
|
|||
if (step > numsteps) {
|
||||
step--;
|
||||
}
|
||||
|
||||
offset += step * numverts;
|
||||
}
|
||||
|
||||
attribute_data_fetch_normals(
|
||||
kg, offset, offset + tri_vindex.x, offset + tri_vindex.y, offset + tri_vindex.z, normals);
|
||||
int i0, i1, i2;
|
||||
|
||||
if (object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
|
||||
if (step != numsteps) {
|
||||
offset += step * kernel_data_fetch(objects, object).numprims * 3;
|
||||
}
|
||||
|
||||
i0 = prim * 3 + 0;
|
||||
i1 = prim * 3 + 1;
|
||||
i2 = prim * 3 + 2;
|
||||
}
|
||||
else {
|
||||
if (step != numsteps) {
|
||||
offset += step * kernel_data_fetch(objects, object).numverts;
|
||||
}
|
||||
|
||||
i0 = tri_vindex.x;
|
||||
i1 = tri_vindex.y;
|
||||
i2 = tri_vindex.z;
|
||||
}
|
||||
|
||||
attribute_data_fetch_normals(kg, offset, i0, i1, i2, normals);
|
||||
}
|
||||
|
||||
ccl_device_inline void motion_triangle_compute_info(KernelGlobals kg,
|
||||
|
|
@ -138,23 +157,28 @@ ccl_device_inline void motion_triangle_vertices(
|
|||
|
||||
ccl_device_inline void motion_triangle_normals(KernelGlobals kg,
|
||||
const int object,
|
||||
const int prim,
|
||||
const uint3 tri_vindex,
|
||||
const int numsteps,
|
||||
const int numverts,
|
||||
const int step,
|
||||
const float t,
|
||||
float3 normals[3])
|
||||
{
|
||||
/* Find attribute. */
|
||||
const int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_NORMAL);
|
||||
const int object_flag = kernel_data_fetch(object_flag, object);
|
||||
const int offset = intersection_find_attribute(kg,
|
||||
object,
|
||||
(object_flag & SD_OBJECT_HAS_CORNER_NORMALS) ?
|
||||
ATTR_STD_MOTION_CORNER_NORMAL :
|
||||
ATTR_STD_MOTION_VERTEX_NORMAL);
|
||||
kernel_assert(offset != ATTR_STD_NOT_FOUND);
|
||||
|
||||
/* Fetch normals. */
|
||||
float3 next_normals[3];
|
||||
motion_triangle_normals_for_step(
|
||||
kg, object, tri_vindex, offset, numverts, numsteps, step, normals);
|
||||
kg, object, object_flag, prim, tri_vindex, offset, numsteps, step, normals);
|
||||
motion_triangle_normals_for_step(
|
||||
kg, object, tri_vindex, offset, numverts, numsteps, step + 1, next_normals);
|
||||
kg, object, object_flag, prim, tri_vindex, offset, numsteps, step + 1, next_normals);
|
||||
|
||||
/* Interpolate between steps. */
|
||||
normals[0] = normalize((1.0f - t) * normals[0] + t * next_normals[0]);
|
||||
|
|
@ -163,26 +187,25 @@ ccl_device_inline void motion_triangle_normals(KernelGlobals kg,
|
|||
}
|
||||
|
||||
ccl_device_inline void motion_triangle_vertices_and_normals(KernelGlobals kg,
|
||||
const int object,
|
||||
const int prim,
|
||||
const float time,
|
||||
const ccl_private ShaderData *sd,
|
||||
float3 verts[3],
|
||||
float3 normals[3])
|
||||
{
|
||||
int numsteps;
|
||||
int step;
|
||||
const int object = sd->object;
|
||||
int numsteps, step;
|
||||
float t;
|
||||
uint3 tri_vindex;
|
||||
motion_triangle_compute_info(kg, object, time, prim, &tri_vindex, &numsteps, &step, &t);
|
||||
motion_triangle_compute_info(kg, object, sd->time, sd->prim, &tri_vindex, &numsteps, &step, &t);
|
||||
|
||||
const int numverts = kernel_data_fetch(objects, object).numverts;
|
||||
motion_triangle_vertices(kg, object, tri_vindex, numsteps, numverts, step, t, verts);
|
||||
motion_triangle_normals(kg, object, tri_vindex, numsteps, numverts, step, t, normals);
|
||||
motion_triangle_normals(kg, object, sd->prim, tri_vindex, numsteps, step, t, normals);
|
||||
}
|
||||
|
||||
ccl_device_inline float3 motion_triangle_smooth_normal(KernelGlobals kg,
|
||||
const float3 Ng,
|
||||
const int object,
|
||||
const int prim,
|
||||
const uint3 tri_vindex,
|
||||
const int numsteps,
|
||||
const int step,
|
||||
|
|
@ -191,8 +214,7 @@ ccl_device_inline float3 motion_triangle_smooth_normal(KernelGlobals kg,
|
|||
const float v)
|
||||
{
|
||||
float3 normals[3];
|
||||
const int numverts = kernel_data_fetch(objects, object).numverts;
|
||||
motion_triangle_normals(kg, object, tri_vindex, numsteps, numverts, step, t, normals);
|
||||
motion_triangle_normals(kg, object, prim, tri_vindex, numsteps, step, t, normals);
|
||||
|
||||
/* Interpolate between normals. */
|
||||
const float w = 1.0f - u - v;
|
||||
|
|
@ -215,7 +237,7 @@ ccl_device_inline float3 motion_triangle_smooth_normal(KernelGlobals kg,
|
|||
uint3 tri_vindex;
|
||||
motion_triangle_compute_info(kg, object, time, prim, &tri_vindex, &numsteps, &step, &t);
|
||||
|
||||
return motion_triangle_smooth_normal(kg, Ng, object, tri_vindex, numsteps, step, t, u, v);
|
||||
return motion_triangle_smooth_normal(kg, Ng, object, prim, tri_vindex, numsteps, step, t, u, v);
|
||||
}
|
||||
|
||||
/* Compute motion triangle normals at the hit position, and offsetted positions in x and y
|
||||
|
|
@ -238,8 +260,7 @@ ccl_device_inline float3 motion_triangle_smooth_normal(KernelGlobals kg,
|
|||
motion_triangle_compute_info(kg, object, time, prim, &tri_vindex, &numsteps, &step, &t);
|
||||
|
||||
float3 n[3];
|
||||
const int numverts = kernel_data_fetch(objects, object).numverts;
|
||||
motion_triangle_normals(kg, object, tri_vindex, numsteps, numverts, step, t, n);
|
||||
motion_triangle_normals(kg, object, prim, tri_vindex, numsteps, step, t, n);
|
||||
|
||||
const float3 N = safe_normalize(triangle_interpolate(u, v, n[0], n[1], n[2]));
|
||||
N_x = safe_normalize(triangle_interpolate(u + du.dx, v + dv.dx, n[0], n[1], n[2]));
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg, ccl_priv
|
|||
/* Compute smooth normal. */
|
||||
if (sd->shader & SHADER_SMOOTH_NORMAL) {
|
||||
sd->N = motion_triangle_smooth_normal(
|
||||
kg, Ng, sd->object, tri_vindex, numsteps, step, t, sd->u, sd->v);
|
||||
kg, Ng, sd->object, sd->prim, tri_vindex, numsteps, step, t, sd->u, sd->v);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -187,7 +187,8 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
|
|||
if (sd->type == PRIMITIVE_TRIANGLE) {
|
||||
/* smooth normal */
|
||||
if (sd->shader & SHADER_SMOOTH_NORMAL) {
|
||||
sd->N = triangle_smooth_normal(kg, Ng, sd->object, sd->prim, sd->u, sd->v);
|
||||
sd->N = triangle_smooth_normal(
|
||||
kg, Ng, sd->object, sd->object_flag, sd->prim, sd->u, sd->v);
|
||||
|
||||
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
object_normal_transform_auto(kg, sd, &sd->N);
|
||||
|
|
|
|||
|
|
@ -105,18 +105,29 @@ ccl_device_inline void triangle_vertices(KernelGlobals kg, const int prim, float
|
|||
|
||||
/* Triangle vertex locations and vertex normals */
|
||||
|
||||
ccl_device_inline void triangle_vertices_and_normals(
|
||||
KernelGlobals kg, const int object, const int prim, float3 P[3], float3 N[3])
|
||||
ccl_device_inline void triangle_vertices_and_normals(KernelGlobals kg,
|
||||
ccl_private const ShaderData *sd,
|
||||
float3 P[3],
|
||||
float3 N[3])
|
||||
{
|
||||
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
|
||||
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, sd->prim);
|
||||
P[0] = kernel_data_fetch(tri_verts, tri_vindex.x);
|
||||
P[1] = kernel_data_fetch(tri_verts, tri_vindex.y);
|
||||
P[2] = kernel_data_fetch(tri_verts, tri_vindex.z);
|
||||
|
||||
const int normal_offset = kernel_data_fetch(objects, object).normal_attr_offset;
|
||||
const int i0 = tri_vindex.x;
|
||||
const int i1 = tri_vindex.y;
|
||||
const int i2 = tri_vindex.z;
|
||||
const int normal_offset = kernel_data_fetch(objects, sd->object).normal_attr_offset;
|
||||
int i0, i1, i2;
|
||||
|
||||
if (sd->object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
|
||||
i0 = sd->prim * 3 + 0;
|
||||
i1 = sd->prim * 3 + 1;
|
||||
i2 = sd->prim * 3 + 2;
|
||||
}
|
||||
else {
|
||||
i0 = tri_vindex.x;
|
||||
i1 = tri_vindex.y;
|
||||
i2 = tri_vindex.z;
|
||||
}
|
||||
|
||||
attribute_data_fetch_normals(kg, normal_offset, i0, i1, i2, N);
|
||||
}
|
||||
|
|
@ -124,28 +135,39 @@ ccl_device_inline void triangle_vertices_and_normals(
|
|||
/* Interpolate smooth vertex normal from vertices */
|
||||
|
||||
ccl_device_forceinline float3 triangle_smooth_normal_unnormalized(
|
||||
KernelGlobals kg, float3 Ng, int object, int prim, float u, float v)
|
||||
KernelGlobals kg, float3 Ng, int object, int object_flag, int prim, float u, float v)
|
||||
{
|
||||
const int normal_offset = kernel_data_fetch(objects, object).normal_attr_offset;
|
||||
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
|
||||
const int i0 = tri_vindex.x;
|
||||
const int i1 = tri_vindex.y;
|
||||
const int i2 = tri_vindex.z;
|
||||
int i0, i1, i2;
|
||||
|
||||
if (object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
|
||||
i0 = prim * 3 + 0;
|
||||
i1 = prim * 3 + 1;
|
||||
i2 = prim * 3 + 2;
|
||||
}
|
||||
else {
|
||||
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
|
||||
i0 = tri_vindex.x;
|
||||
i1 = tri_vindex.y;
|
||||
i2 = tri_vindex.z;
|
||||
}
|
||||
|
||||
const float3 N = attribute_data_interpolate_normals(kg, normal_offset, i0, i1, i2, u, v);
|
||||
return is_zero(N) ? Ng : N;
|
||||
}
|
||||
|
||||
ccl_device_inline float3
|
||||
triangle_smooth_normal(KernelGlobals kg, float3 Ng, int object, int prim, float u, float v)
|
||||
ccl_device_inline float3 triangle_smooth_normal(
|
||||
KernelGlobals kg, float3 Ng, int object, int object_flag, int prim, float u, float v)
|
||||
{
|
||||
return safe_normalize(triangle_smooth_normal_unnormalized(kg, Ng, object, prim, u, v));
|
||||
return safe_normalize(
|
||||
triangle_smooth_normal_unnormalized(kg, Ng, object, object_flag, prim, u, v));
|
||||
}
|
||||
|
||||
ccl_device_inline float3 triangle_smooth_normal_unnormalized(KernelGlobals kg,
|
||||
ccl_private const ShaderData *sd)
|
||||
{
|
||||
return triangle_smooth_normal_unnormalized(kg, sd->Ng, sd->object, sd->prim, sd->u, sd->v);
|
||||
return triangle_smooth_normal_unnormalized(
|
||||
kg, sd->Ng, sd->object, sd->object_flag, sd->prim, sd->u, sd->v);
|
||||
}
|
||||
|
||||
/* Compute triangle normals at the hit position, and offsetted positions in x and y direction for
|
||||
|
|
@ -153,6 +175,7 @@ ccl_device_inline float3 triangle_smooth_normal_unnormalized(KernelGlobals kg,
|
|||
ccl_device_inline float3 triangle_smooth_normal(KernelGlobals kg,
|
||||
const float3 Ng,
|
||||
const int object,
|
||||
const int object_flag,
|
||||
const int prim,
|
||||
const float u,
|
||||
float v,
|
||||
|
|
@ -162,11 +185,19 @@ ccl_device_inline float3 triangle_smooth_normal(KernelGlobals kg,
|
|||
ccl_private float3 &N_y)
|
||||
{
|
||||
const int normal_offset = kernel_data_fetch(objects, object).normal_attr_offset;
|
||||
int i0, i1, i2;
|
||||
|
||||
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
|
||||
const int i0 = tri_vindex.x;
|
||||
const int i1 = tri_vindex.y;
|
||||
const int i2 = tri_vindex.z;
|
||||
if (object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
|
||||
i0 = prim * 3 + 0;
|
||||
i1 = prim * 3 + 1;
|
||||
i2 = prim * 3 + 2;
|
||||
}
|
||||
else {
|
||||
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
|
||||
i0 = tri_vindex.x;
|
||||
i1 = tri_vindex.y;
|
||||
i2 = tri_vindex.z;
|
||||
}
|
||||
|
||||
float3 n[3];
|
||||
attribute_data_fetch_normals(kg, normal_offset, i0, i1, i2, n);
|
||||
|
|
@ -227,12 +258,6 @@ ccl_device_inline T triangle_attribute_dfdy(const ccl_private differential &du,
|
|||
return du.dy * f1 + dv.dy * f2 - (du.dy + dv.dy) * f0;
|
||||
}
|
||||
|
||||
#ifdef __KERNEL_METAL__
|
||||
template<typename U, typename V> using triangle_attribute_is_same = metal::is_same<U, V>;
|
||||
#else
|
||||
template<typename U, typename V> using triangle_attribute_is_same = std::is_same<U, V>;
|
||||
#endif
|
||||
|
||||
/* Read attributes on various triangle elements, and compute the partial derivatives if requested.
|
||||
*/
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ ccl_device_inline void triangle_shader_setup(KernelGlobals kg, ccl_private Shade
|
|||
|
||||
/* Smooth normal. */
|
||||
if (sd->shader & SHADER_SMOOTH_NORMAL) {
|
||||
sd->N = triangle_smooth_normal(kg, Ng, sd->object, sd->prim, sd->u, sd->v);
|
||||
sd->N = triangle_smooth_normal(kg, Ng, sd->object, sd->object_flag, sd->prim, sd->u, sd->v);
|
||||
}
|
||||
|
||||
#ifdef __DPDU__
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
|
|||
else {
|
||||
/* Surface baking. */
|
||||
float3 N = (shader & SHADER_SMOOTH_NORMAL) ?
|
||||
triangle_smooth_normal(kg, Ng, object, prim, u, v) :
|
||||
triangle_smooth_normal(kg, Ng, object, object_flag, prim, u, v) :
|
||||
Ng;
|
||||
|
||||
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ ccl_device_inline void mnee_setup_manifold_vertex(KernelGlobals kg,
|
|||
float3 normals[3];
|
||||
if (sd_vtx->type & PRIMITIVE_TRIANGLE) {
|
||||
/* Load triangle vertices and normals. */
|
||||
triangle_vertices_and_normals(kg, sd_vtx->object, sd_vtx->prim, verts, normals);
|
||||
triangle_vertices_and_normals(kg, sd_vtx, verts, normals);
|
||||
|
||||
/* Compute refined position (same code as in triangle_point_from_uv). */
|
||||
sd_vtx->P = (1.f - isect->u - isect->v) * verts[0] + isect->u * verts[1] + isect->v * verts[2];
|
||||
|
|
@ -160,8 +160,7 @@ ccl_device_inline void mnee_setup_manifold_vertex(KernelGlobals kg,
|
|||
}
|
||||
else { /* if (sd_vtx->type & PRIMITIVE_MOTION_TRIANGLE) */
|
||||
/* Load triangle vertices and normals. */
|
||||
motion_triangle_vertices_and_normals(
|
||||
kg, sd_vtx->object, sd_vtx->prim, sd_vtx->time, verts, normals);
|
||||
motion_triangle_vertices_and_normals(kg, sd_vtx, verts, normals);
|
||||
|
||||
/* Compute refined position. */
|
||||
sd_vtx->P = motion_triangle_point_from_uv(kg, sd_vtx, isect->u, isect->v, verts);
|
||||
|
|
|
|||
|
|
@ -166,11 +166,11 @@ ccl_device_inline float3 shadow_ray_smooth_surface_offset(
|
|||
float3 N[3];
|
||||
|
||||
if (sd->type == PRIMITIVE_MOTION_TRIANGLE) {
|
||||
motion_triangle_vertices_and_normals(kg, sd->object, sd->prim, sd->time, V, N);
|
||||
motion_triangle_vertices_and_normals(kg, sd, V, N);
|
||||
}
|
||||
else {
|
||||
kernel_assert(sd->type == PRIMITIVE_TRIANGLE);
|
||||
triangle_vertices_and_normals(kg, sd->object, sd->prim, V, N);
|
||||
triangle_vertices_and_normals(kg, sd, V, N);
|
||||
}
|
||||
|
||||
const float u = 1.0f - sd->u - sd->v;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ ccl_device bool attribute_bump_map_normal(KernelGlobals kg,
|
|||
|
||||
if (sd->type == PRIMITIVE_TRIANGLE) {
|
||||
f.val = triangle_smooth_normal(
|
||||
kg, Ng, sd->object, sd->prim, sd->u, sd->v, sd->du, sd->dv, f.dx, f.dy);
|
||||
kg, Ng, sd->object, sd->object_flag, sd->prim, sd->u, sd->v, sd->du, sd->dv, f.dx, f.dy);
|
||||
}
|
||||
else {
|
||||
assert(sd->type & PRIMITIVE_MOTION_TRIANGLE);
|
||||
|
|
|
|||
|
|
@ -241,11 +241,11 @@ ccl_device float3 svm_bevel(
|
|||
const float v = isect.hits[hit].v;
|
||||
|
||||
if (sd->type == PRIMITIVE_TRIANGLE) {
|
||||
N = triangle_smooth_normal(kg, N, object, prim, u, v);
|
||||
N = triangle_smooth_normal(kg, N, object, object_flag, prim, u, v);
|
||||
}
|
||||
# ifdef __OBJECT_MOTION__
|
||||
else if (sd->type == PRIMITIVE_MOTION_TRIANGLE) {
|
||||
N = motion_triangle_smooth_normal(kg, N, sd->object, prim, u, v, sd->time);
|
||||
N = motion_triangle_smooth_normal(kg, N, object, prim, u, v, sd->time);
|
||||
}
|
||||
# endif /* __OBJECT_MOTION__ */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ ccl_device RaycastResult svm_raycast(KernelGlobals kg,
|
|||
/* Compute smooth normal. */
|
||||
if (shader & SHADER_SMOOTH_NORMAL) {
|
||||
if (isect.type == PRIMITIVE_TRIANGLE) {
|
||||
result.normal = triangle_smooth_normal(kg, Ng, object, prim, u, v);
|
||||
result.normal = triangle_smooth_normal(kg, Ng, object, object_flag, prim, u, v);
|
||||
}
|
||||
# ifdef __OBJECT_MOTION__
|
||||
else if (isect.type == PRIMITIVE_MOTION_TRIANGLE) {
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ ccl_device_inline float3 texco_normal_from_uv(KernelGlobals kg,
|
|||
float3 N;
|
||||
if ((sd->type & PRIMITIVE_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) {
|
||||
N = (sd->type == PRIMITIVE_TRIANGLE) ?
|
||||
triangle_smooth_normal(kg, zero_float3(), sd->object, sd->prim, u, v) :
|
||||
triangle_smooth_normal(
|
||||
kg, zero_float3(), sd->object, sd->object_flag, sd->prim, u, v) :
|
||||
motion_triangle_smooth_normal(kg, zero_float3(), sd->object, sd->prim, u, v, sd->time);
|
||||
if (is_zero(N)) {
|
||||
N = sd->Ng;
|
||||
|
|
|
|||
|
|
@ -696,6 +696,9 @@ enum AttributeElement {
|
|||
ATTR_ELEMENT_IS_MOTION,
|
||||
|
||||
ATTR_ELEMENT_CORNER_BYTE = ATTR_ELEMENT_CORNER | ATTR_ELEMENT_IS_BYTE,
|
||||
ATTR_ELEMENT_CORNER_NORMAL = ATTR_ELEMENT_CORNER | ATTR_ELEMENT_IS_NORMAL,
|
||||
ATTR_ELEMENT_CORNER_NORMAL_MOTION = ATTR_ELEMENT_CORNER | ATTR_ELEMENT_IS_NORMAL |
|
||||
ATTR_ELEMENT_IS_MOTION,
|
||||
|
||||
ATTR_ELEMENT_CURVE_KEY_MOTION = ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_IS_MOTION,
|
||||
ATTR_ELEMENT_CURVE_KEY_NORMAL = ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_IS_NORMAL,
|
||||
|
|
@ -944,6 +947,8 @@ enum ShaderDataObjectFlag : uint {
|
|||
SD_OBJECT_CAUSTICS_RECEIVER = (1u << 10),
|
||||
/* object has attribute for volume motion */
|
||||
SD_OBJECT_HAS_VOLUME_MOTION = (1u << 11),
|
||||
/* Geometry has per-corner normals instead of per-vertex. */
|
||||
SD_OBJECT_HAS_CORNER_NORMALS = (1u << 12),
|
||||
|
||||
/* object is using caustics */
|
||||
SD_OBJECT_CAUSTICS = (SD_OBJECT_CAUSTICS_CASTER | SD_OBJECT_CAUSTICS_RECEIVER),
|
||||
|
|
@ -952,7 +957,7 @@ enum ShaderDataObjectFlag : uint {
|
|||
SD_OBJECT_NEGATIVE_SCALE | SD_OBJECT_HAS_VOLUME |
|
||||
SD_OBJECT_INTERSECTS_VOLUME | SD_OBJECT_SHADOW_CATCHER |
|
||||
SD_OBJECT_HAS_VOLUME_ATTRIBUTES | SD_OBJECT_CAUSTICS |
|
||||
SD_OBJECT_HAS_VOLUME_MOTION)
|
||||
SD_OBJECT_HAS_VOLUME_MOTION | SD_OBJECT_HAS_CORNER_NORMALS)
|
||||
};
|
||||
|
||||
struct ccl_align(16) ShaderData {
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
|
|||
if (geom->is_mesh() || geom->is_volume()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (prim == ATTR_PRIM_SUBD) {
|
||||
size = mesh->get_verts().size();
|
||||
size = mesh->get_num_subd_base_verts();
|
||||
}
|
||||
else {
|
||||
size = mesh->get_verts().size();
|
||||
|
|
@ -264,6 +264,8 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const
|
|||
break;
|
||||
case ATTR_ELEMENT_CORNER:
|
||||
case ATTR_ELEMENT_CORNER_BYTE:
|
||||
case ATTR_ELEMENT_CORNER_NORMAL:
|
||||
case ATTR_ELEMENT_CORNER_NORMAL_MOTION:
|
||||
if (geom->is_mesh()) {
|
||||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (prim == ATTR_PRIM_SUBD) {
|
||||
|
|
@ -577,6 +579,12 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
|
|||
case ATTR_STD_MOTION_VERTEX_NORMAL:
|
||||
attr = add(name, TypeNormal, ATTR_ELEMENT_VERTEX_NORMAL_MOTION);
|
||||
break;
|
||||
case ATTR_STD_CORNER_NORMAL:
|
||||
attr = add(name, TypeNormal, ATTR_ELEMENT_CORNER_NORMAL);
|
||||
break;
|
||||
case ATTR_STD_MOTION_CORNER_NORMAL:
|
||||
attr = add(name, TypeNormal, ATTR_ELEMENT_CORNER_NORMAL_MOTION);
|
||||
break;
|
||||
case ATTR_STD_PTEX_FACE_ID:
|
||||
attr = add(name, TypeFloat, ATTR_ELEMENT_FACE);
|
||||
break;
|
||||
|
|
@ -624,6 +632,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
|
|||
case ATTR_STD_VERTEX_NORMAL:
|
||||
attr = add(name, TypeNormal, ATTR_ELEMENT_VERTEX_NORMAL);
|
||||
break;
|
||||
case ATTR_STD_CORNER_NORMAL:
|
||||
attr = add(name, TypeNormal, ATTR_ELEMENT_CORNER_NORMAL);
|
||||
break;
|
||||
case ATTR_STD_VOLUME_DENSITY:
|
||||
case ATTR_STD_VOLUME_FLAME:
|
||||
case ATTR_STD_VOLUME_HEAT:
|
||||
|
|
|
|||
|
|
@ -940,6 +940,7 @@ void GeometryManager::device_update(Device *device,
|
|||
Mesh *mesh = static_cast<Mesh *>(geom);
|
||||
if (displace(device, scene, mesh, progress)) {
|
||||
displacement_done = true;
|
||||
need_flags_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -472,6 +472,8 @@ void GeometryManager::device_update_attributes(Device *device,
|
|||
switch (attr.std) {
|
||||
case ATTR_STD_VERTEX_NORMAL:
|
||||
case ATTR_STD_MOTION_VERTEX_NORMAL:
|
||||
case ATTR_STD_CORNER_NORMAL:
|
||||
case ATTR_STD_MOTION_CORNER_NORMAL:
|
||||
case ATTR_STD_SHADOW_TRANSPARENCY:
|
||||
geom_attributes[i].add(attr.std);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -29,11 +29,13 @@ CCL_NAMESPACE_BEGIN
|
|||
struct MikkMeshWrapper {
|
||||
MikkMeshWrapper(const Mesh *mesh,
|
||||
const packed_normal *vertex_normal,
|
||||
const packed_normal *corner_normal,
|
||||
const float2 *uv,
|
||||
float3 *tangent,
|
||||
float *tangent_sign)
|
||||
: mesh(mesh),
|
||||
vertex_normal(vertex_normal),
|
||||
corner_normal(corner_normal),
|
||||
uv(uv),
|
||||
tangent(tangent),
|
||||
tangent_sign(tangent_sign)
|
||||
|
|
@ -86,7 +88,9 @@ struct MikkMeshWrapper {
|
|||
{
|
||||
float3 vN;
|
||||
if (mesh->get_smooth()[face_num]) {
|
||||
vN = vertex_normal[VertexIndex(face_num, vert_num)].decode();
|
||||
vN = ((corner_normal) ? corner_normal[CornerIndex(face_num, vert_num)] :
|
||||
vertex_normal[VertexIndex(face_num, vert_num)])
|
||||
.decode();
|
||||
}
|
||||
else {
|
||||
const Mesh::Triangle tri = mesh->get_triangle(face_num);
|
||||
|
|
@ -112,6 +116,7 @@ struct MikkMeshWrapper {
|
|||
const Mesh *mesh;
|
||||
|
||||
const packed_normal *vertex_normal;
|
||||
const packed_normal *corner_normal;
|
||||
const float2 *uv;
|
||||
|
||||
float3 *tangent;
|
||||
|
|
@ -130,12 +135,14 @@ static void mikk_compute_tangents(Attribute *attr_uv,
|
|||
AttributeSet &attributes = mesh->attributes;
|
||||
|
||||
Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL);
|
||||
if (attr_vN == nullptr) {
|
||||
Attribute *attr_cN = attributes.find(ATTR_STD_CORNER_NORMAL);
|
||||
if (attr_vN == nullptr && attr_cN == nullptr) {
|
||||
/* no normals */
|
||||
return;
|
||||
}
|
||||
|
||||
const packed_normal *vertex_normal = attr_vN ? attr_vN->data_normal() : nullptr;
|
||||
const packed_normal *corner_normal = attr_cN ? attr_cN->data_normal() : nullptr;
|
||||
const float2 *uv = (attr_uv) ? attr_uv->data_float2() : nullptr;
|
||||
|
||||
const ustring name = ustring((attr_uv) ? attr_uv->name.string() + tangent_postfix :
|
||||
|
|
@ -163,7 +170,7 @@ static void mikk_compute_tangents(Attribute *attr_uv,
|
|||
tangent_sign = attr_sign->data_float();
|
||||
}
|
||||
|
||||
MikkMeshWrapper userdata(mesh, vertex_normal, uv, tangent, tangent_sign);
|
||||
MikkMeshWrapper userdata(mesh, vertex_normal, corner_normal, uv, tangent, tangent_sign);
|
||||
/* Compute tangents. */
|
||||
mikk::Mikktspace(userdata).genTangSpace();
|
||||
}
|
||||
|
|
@ -545,6 +552,14 @@ void Mesh::copy_center_to_motion_step(const int motion_step)
|
|||
const packed_normal *N = attr_N->data_normal();
|
||||
std::copy_n(N, numverts, attr_mN->data_normal() + motion_step * numverts);
|
||||
}
|
||||
|
||||
Attribute *attr_mcN = attributes.find(ATTR_STD_MOTION_CORNER_NORMAL);
|
||||
Attribute *attr_cN = attributes.find(ATTR_STD_CORNER_NORMAL);
|
||||
if (attr_mcN && attr_cN) {
|
||||
const size_t numcorners = triangles.size();
|
||||
packed_normal *N = attr_cN->data_normal();
|
||||
std::copy_n(N, numcorners, attr_mcN->data_normal() + motion_step * numcorners);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -640,6 +655,17 @@ void Mesh::apply_transform(const Transform &tfm, const bool apply_to_motion)
|
|||
}
|
||||
}
|
||||
|
||||
Attribute *attr_cN = attributes.find(ATTR_STD_CORNER_NORMAL);
|
||||
if (attr_cN) {
|
||||
const Transform ntfm = transform_normal;
|
||||
const size_t num_corners = triangles.size();
|
||||
packed_normal *cN = attr_cN->data_normal();
|
||||
|
||||
for (size_t i = 0; i < num_corners; i++) {
|
||||
cN[i] = packed_normal(normalize(transform_direction(&ntfm, cN[i].decode())));
|
||||
}
|
||||
}
|
||||
|
||||
if (apply_to_motion) {
|
||||
Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
|
||||
|
||||
|
|
@ -664,11 +690,36 @@ void Mesh::apply_transform(const Transform &tfm, const bool apply_to_motion)
|
|||
normalize(transform_direction(&ntfm, normal_steps[i].decode())));
|
||||
}
|
||||
}
|
||||
|
||||
Attribute *attr_mcN = attributes.find(ATTR_STD_MOTION_CORNER_NORMAL);
|
||||
|
||||
if (attr_mcN) {
|
||||
const Transform ntfm = transform_normal;
|
||||
const size_t steps_size = triangles.size() * (motion_steps - 1);
|
||||
packed_normal *normal_steps = attr_mcN->data_normal();
|
||||
|
||||
for (size_t i = 0; i < steps_size; i++) {
|
||||
normal_steps[i] = packed_normal(
|
||||
normalize(transform_direction(&ntfm, normal_steps[i].decode())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::add_vertex_normals()
|
||||
{
|
||||
if (attributes.find(ATTR_STD_CORNER_NORMAL)) {
|
||||
/* Not needed if we already have corner normals overriding these.
|
||||
* If there is motion blur without motion corner normals we can't
|
||||
* render correctly, discard corner normals. */
|
||||
if (has_motion_blur() && !attributes.find(ATTR_STD_MOTION_CORNER_NORMAL)) {
|
||||
attributes.remove(ATTR_STD_CORNER_NORMAL);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const bool flip = transform_negative_scaled;
|
||||
const size_t verts_size = verts.size();
|
||||
const size_t triangles_size = num_triangles();
|
||||
|
|
@ -822,7 +873,7 @@ void Mesh::update_tangents(Scene *scene, bool undisplaced)
|
|||
return;
|
||||
}
|
||||
|
||||
assert(attributes.find(ATTR_STD_VERTEX_NORMAL));
|
||||
assert(attributes.find(ATTR_STD_VERTEX_NORMAL) || attributes.find(ATTR_STD_CORNER_NORMAL));
|
||||
|
||||
ccl::set<ustring> uv_maps;
|
||||
Attribute *attr_std_uv = attributes.find(ATTR_STD_UV);
|
||||
|
|
|
|||
|
|
@ -187,6 +187,20 @@ bool GeometryManager::displace(Device *device, Scene *scene, Mesh *mesh, Progres
|
|||
bool need_recompute_vertex_normals = false;
|
||||
bool need_recompute_all_vertex_normals = false;
|
||||
|
||||
/* Corner normals can't be preserved through displacement. */
|
||||
if (mesh->attributes.find(ATTR_STD_CORNER_NORMAL)) {
|
||||
mesh->attributes.remove(ATTR_STD_CORNER_NORMAL);
|
||||
mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
need_recompute_vertex_normals = true;
|
||||
need_recompute_all_vertex_normals = true;
|
||||
}
|
||||
if (mesh->attributes.find(ATTR_STD_MOTION_CORNER_NORMAL)) {
|
||||
mesh->attributes.remove(ATTR_STD_MOTION_CORNER_NORMAL);
|
||||
mesh->attributes.add(ATTR_STD_MOTION_VERTEX_NORMAL);
|
||||
need_recompute_vertex_normals = true;
|
||||
need_recompute_all_vertex_normals = true;
|
||||
}
|
||||
|
||||
for (Node *node : mesh->get_used_shaders()) {
|
||||
Shader *shader = static_cast<Shader *>(node);
|
||||
if (shader->has_displacement && shader->get_displacement_method() == DISPLACE_TRUE) {
|
||||
|
|
@ -207,7 +221,7 @@ bool GeometryManager::displace(Device *device, Scene *scene, Mesh *mesh, Progres
|
|||
scene->default_surface;
|
||||
|
||||
tri_recompute[i] = shader->has_displacement &&
|
||||
shader->get_displacement_method() == DISPLACE_TRUE;
|
||||
shader->get_displacement_method() == DISPLACE_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "scene/object.h"
|
||||
|
||||
#include "device/device.h"
|
||||
#include "kernel/types.h"
|
||||
#include "scene/camera.h"
|
||||
#include "scene/curves.h"
|
||||
#include "scene/hair.h"
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
#include "scene/particles.h"
|
||||
#include "scene/pointcloud.h"
|
||||
#include "scene/scene.h"
|
||||
#include "scene/shader.h"
|
||||
#include "scene/stats.h"
|
||||
#include "scene/volume.h"
|
||||
|
||||
|
|
@ -558,6 +560,9 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
|
|||
{
|
||||
flag |= SD_OBJECT_HAS_VERTEX_MOTION;
|
||||
}
|
||||
else if (mesh->attributes.find(ATTR_STD_CORNER_NORMAL)) {
|
||||
flag |= SD_OBJECT_HAS_CORNER_NORMALS;
|
||||
}
|
||||
}
|
||||
else if (geom->is_volume()) {
|
||||
Volume *volume = static_cast<Volume *>(geom);
|
||||
|
|
@ -949,6 +954,14 @@ void ObjectManager::device_update_flags(Device * /*unused*/,
|
|||
object_flag[object->index] &= ~SD_OBJECT_SHADOW_CATCHER;
|
||||
}
|
||||
|
||||
/* Corner normals might get removed by subdivision and displacement. */
|
||||
if (object->geometry->attributes.find(ATTR_STD_CORNER_NORMAL)) {
|
||||
object_flag[object->index] |= SD_OBJECT_HAS_CORNER_NORMALS;
|
||||
}
|
||||
else {
|
||||
object_flag[object->index] &= ~SD_OBJECT_HAS_CORNER_NORMALS;
|
||||
}
|
||||
|
||||
if (bounds_valid) {
|
||||
object->intersects_volume = false;
|
||||
for (Object *volume_object : volume_objects) {
|
||||
|
|
@ -979,7 +992,7 @@ void ObjectManager::device_update_geom_offsets(Device * /*unused*/,
|
|||
DeviceScene *dscene,
|
||||
Scene *scene)
|
||||
{
|
||||
if (scene->objects.size() == 0) {
|
||||
if (dscene->objects.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -997,6 +1010,7 @@ void ObjectManager::device_update_geom_offsets(Device * /*unused*/,
|
|||
if (attr_map_offset == 0) {
|
||||
attr_map_offset = geom->attr_map_offset;
|
||||
}
|
||||
|
||||
if (kobject.attribute_map_offset != attr_map_offset) {
|
||||
kobject.attribute_map_offset = attr_map_offset;
|
||||
update = true;
|
||||
|
|
@ -1008,8 +1022,15 @@ void ObjectManager::device_update_geom_offsets(Device * /*unused*/,
|
|||
normal_attr_offset = find_attribute(dscene->attributes_map.data(),
|
||||
attr_map_offset,
|
||||
PRIMITIVE_TRIANGLE,
|
||||
ATTR_STD_VERTEX_NORMAL)
|
||||
ATTR_STD_CORNER_NORMAL)
|
||||
.offset;
|
||||
if (normal_attr_offset == ATTR_STD_NOT_FOUND) {
|
||||
normal_attr_offset = find_attribute(dscene->attributes_map.data(),
|
||||
attr_map_offset,
|
||||
PRIMITIVE_TRIANGLE,
|
||||
ATTR_STD_VERTEX_NORMAL)
|
||||
.offset;
|
||||
}
|
||||
assert(normal_attr_offset != ATTR_STD_NOT_FOUND ||
|
||||
static_cast<Mesh *>(geom)->num_triangles() == 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ bool SubdAttributeInterpolation::support_interp_attribute(const Attribute &attr)
|
|||
/* Smooth normals are computed from derivatives, for linear interpolate. */
|
||||
case ATTR_STD_VERTEX_NORMAL:
|
||||
case ATTR_STD_MOTION_VERTEX_NORMAL:
|
||||
case ATTR_STD_CORNER_NORMAL:
|
||||
case ATTR_STD_MOTION_CORNER_NORMAL:
|
||||
if (mesh.get_subdivision_type() == Mesh::SUBDIVISION_CATMULL_CLARK) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -110,11 +112,13 @@ bool SubdAttributeInterpolation::support_interp_attribute(const Attribute &attr)
|
|||
/* Skip element types that should not exist for subd attributes anyway. */
|
||||
switch (attr.element) {
|
||||
case ATTR_ELEMENT_VERTEX:
|
||||
case ATTR_ELEMENT_VERTEX_MOTION:
|
||||
case ATTR_ELEMENT_VERTEX_NORMAL:
|
||||
case ATTR_ELEMENT_VERTEX_NORMAL_MOTION:
|
||||
case ATTR_ELEMENT_CORNER:
|
||||
case ATTR_ELEMENT_CORNER_BYTE:
|
||||
case ATTR_ELEMENT_VERTEX_MOTION:
|
||||
case ATTR_ELEMENT_VERTEX_NORMAL_MOTION:
|
||||
case ATTR_ELEMENT_CORNER_NORMAL:
|
||||
case ATTR_ELEMENT_CORNER_NORMAL_MOTION:
|
||||
case ATTR_ELEMENT_FACE:
|
||||
break;
|
||||
default:
|
||||
|
|
@ -334,13 +338,17 @@ void SubdAttributeInterpolation::setup_attribute_vertex_smooth(const Attribute &
|
|||
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::setup_attribute_corner_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step)
|
||||
{
|
||||
SubdAttribute attr;
|
||||
|
||||
/* Interpolate values at corners. */
|
||||
const typename T::Type *subd_data = reinterpret_cast<const typename T::Type *>(subd_attr.data());
|
||||
typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data());
|
||||
const typename T::Type *subd_data = reinterpret_cast<const typename T::Type *>(
|
||||
subd_attr.data()) +
|
||||
motion_step * mesh.get_subd_face_corners().size();
|
||||
typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data()) +
|
||||
motion_step * mesh.num_triangles() * 3;
|
||||
|
||||
assert(mesh_data != nullptr);
|
||||
|
||||
|
|
@ -533,8 +541,6 @@ void SubdAttributeInterpolation::setup_attribute_type(const Attribute &subd_attr
|
|||
case ATTR_STD_GENERATED:
|
||||
case ATTR_STD_POSITION_UNDEFORMED:
|
||||
case ATTR_STD_POSITION_UNDISPLACED:
|
||||
case ATTR_STD_VERTEX_NORMAL:
|
||||
case ATTR_STD_NORMAL_UNDISPLACED:
|
||||
setup_attribute_vertex_smooth<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -550,7 +556,8 @@ void SubdAttributeInterpolation::setup_attribute_type(const Attribute &subd_attr
|
|||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_CORNER:
|
||||
case ATTR_ELEMENT_CORNER_BYTE: {
|
||||
case ATTR_ELEMENT_CORNER_BYTE:
|
||||
case ATTR_ELEMENT_CORNER_NORMAL: {
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
if (osd_mesh.use_smooth_fvar(subd_attr)) {
|
||||
for (const auto &merged_fvar : osd_mesh.merged_fvars) {
|
||||
|
|
@ -582,6 +589,13 @@ void SubdAttributeInterpolation::setup_attribute_type(const Attribute &subd_attr
|
|||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_CORNER_NORMAL_MOTION: {
|
||||
/* Interpolate each motion step individually. */
|
||||
for (int step = 0; step < mesh.get_motion_steps() - 1; step++) {
|
||||
setup_attribute_corner_linear<T>(subd_attr, mesh_attr, step);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_FACE: {
|
||||
setup_attribute_face<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,9 @@ class SubdAttributeInterpolation {
|
|||
const int motion_step = 0);
|
||||
|
||||
template<typename T>
|
||||
void setup_attribute_corner_linear(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
void setup_attribute_corner_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step = 0);
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
template<typename T>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue