struct PrimAttribBlur : INode {
void apply() override {
auto prim = get_input<PrimitiveObject>("prim");
auto prim_type = get_input2<std::string>("primType");
// auto maskName = get_input2<std::string>("group");
// if (!prim->verts.has_attr(maskName)) {
// auto &_mask = prim->verts.add_attr<float>(maskName);
// std::fill(_mask.begin(), _mask.end(), 1.0);
// }
// auto &mask = prim->verts.attr<float>(maskName);
auto attr_name = get_input2<std::string>("attributes");
auto attr_type = get_input2<std::string>("attributesType");
auto useEdgeLength = get_input<NumericObject>("useEdgeLengthWeight")->get<bool>();
auto iterations = get_input<NumericObject>("blurringIterations")->get<int>();
auto mode = get_input2<std::string>("mode");
auto mu = get_input<NumericObject>("stepSize")->get<float>();
auto lambda = mu;
if (mode == "VolumePreserving") {
auto passband = get_input<NumericObject>("cutoffFrequency")->get<float>();
if (passband < 1e-5) {
float sqrt2_5 = 0.6324555320336758664;
lambda = sqrt2_5;
mu = -sqrt2_5;
} else {
// See:
// Gabriel Taubin. "A signal processing approach to fair surface design".
// SIGGRAPH 1995
//
// Let l be lambda, u be mu, and b be the passband frequency.
// f(k) = (1-k*l)(1-k*u). This function has maximum at 1/l + 1/u.
// We want the maximum to occur at b, so we have the constraint
// 1/l + 1/u = passband
// so u = l/(bl - 1)
// We also want f(1) = -f(2). This gives the constraint:
// u = (2-3l)/(3-5l)
// Equating this equations gives:
// (3b-5)l^2 - 2bl + 2 = 0
// Solve for l using the quadratic formula. We want l>0. Since b>0 and
// 6b-10<0, subtract the square root of the discriminant to get a negative
// numerator so the quotient is positive.
auto discriminant = 4.0 * passband * passband - 24.0 * passband + 40.0;
lambda = 2.0 * passband - sqrt(discriminant);
lambda /= (6.0 * passband - 10.0);
// Now solve for u. Note that u is also the other root of the quadratic.
mu = lambda / (passband * lambda - 1.0);
}
} else if (mode == "custom") {
lambda = get_input<NumericObject>("oddStepSize")->get<float>();
mu = get_input<NumericObject>("evenStepSize")->get<float>();
}
auto weightName = get_input2<std::string>("weightAttributes");
if (!prim->verts.has_attr(weightName)) {
auto &_weight = prim->verts.add_attr<float>(weightName);
std::fill(_weight.begin(), _weight.end(), 1.0);
}
auto &weight = prim->verts.attr<float>(weightName);
// 找临近点,假设最多 8 个临近点
auto &neighbor_0 = prim->verts.add_attr<int>("_neighbor_0");
auto &neighbor_1 = prim->verts.add_attr<int>("_neighbor_1");
auto &neighbor_2 = prim->verts.add_attr<int>("_neighbor_2");
auto &neighbor_3 = prim->verts.add_attr<int>("_neighbor_3");
auto &neighbor_4 = prim->verts.add_attr<int>("_neighbor_4");
auto &neighbor_5 = prim->verts.add_attr<int>("_neighbor_5");
auto &neighbor_6 = prim->verts.add_attr<int>("_neighbor_6");
auto &neighbor_7 = prim->verts.add_attr<int>("_neighbor_7");
std::fill(neighbor_0.begin(), neighbor_0.end(), -1);
std::fill(neighbor_1.begin(), neighbor_1.end(), -1);
std::fill(neighbor_2.begin(), neighbor_2.end(), -1);
std::fill(neighbor_3.begin(), neighbor_3.end(), -1);
std::fill(neighbor_4.begin(), neighbor_4.end(), -1);
std::fill(neighbor_5.begin(), neighbor_5.end(), -1);
std::fill(neighbor_6.begin(), neighbor_6.end(), -1);
std::fill(neighbor_7.begin(), neighbor_7.end(), -1);
auto &edgeweight_0 = prim->verts.add_attr<float>("_edgeweight_0");
auto &edgeweight_1 = prim->verts.add_attr<float>("_edgeweight_1");
auto &edgeweight_2 = prim->verts.add_attr<float>("_edgeweight_2");
auto &edgeweight_3 = prim->verts.add_attr<float>("_edgeweight_3");
auto &edgeweight_4 = prim->verts.add_attr<float>("_edgeweight_4");
auto &edgeweight_5 = prim->verts.add_attr<float>("_edgeweight_5");
auto &edgeweight_6 = prim->verts.add_attr<float>("_edgeweight_6");
auto &edgeweight_7 = prim->verts.add_attr<float>("_edgeweight_7");
std::fill(edgeweight_0.begin(), edgeweight_0.end(), 0);
std::fill(edgeweight_1.begin(), edgeweight_1.end(), 0);
std::fill(edgeweight_2.begin(), edgeweight_2.end(), 0);
std::fill(edgeweight_3.begin(), edgeweight_3.end(), 0);
std::fill(edgeweight_4.begin(), edgeweight_4.end(), 0);
std::fill(edgeweight_5.begin(), edgeweight_5.end(), 0);
std::fill(edgeweight_6.begin(), edgeweight_6.end(), 0);
std::fill(edgeweight_7.begin(), edgeweight_7.end(), 0);
//========================================
// LARGE_INTEGER t1_0,t2_0,tc_0;
// LARGE_INTEGER t1_1,t2_1,tc_1;
// LARGE_INTEGER t1_2,t2_2,tc_2;
//========================================
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// QueryPerformanceFrequency(&tc_0);
// QueryPerformanceCounter(&t1_0);
#pragma omp parallel for
for (size_t point_idx = 0; point_idx < prim->verts.size(); point_idx++) { // 遍历所有点,找它的邻居
std::map<std::string, int> neighborVertID;
std::map<std::string, float> neighborEdgeLength;
for(int i = 0; i < 8; i++) {
neighborVertID["neighbor_" + std::to_string(i)] = -1;
neighborEdgeLength["edgeweight_" + std::to_string(i)] = 0;
}
int find_neighbor_count = 0;
float edgeLengthSum = 0;
volatile bool flag = false;
if (prim_type == "line") {
#pragma omp parallel for shared(flag)
for (size_t line_idx = 0; line_idx < prim->lines.size(); line_idx++) {
if(flag) continue;
if (prim->lines[line_idx][0] == point_idx) {
neighborVertID["neighbor_" + std::to_string(find_neighbor_count)] = prim->lines[line_idx][1];
if (useEdgeLength) {
float edgeLength = length(prim->verts[prim->lines[line_idx][1]] - prim->verts[point_idx]);
neighborEdgeLength["edgeweight_" + std::to_string(find_neighbor_count)] = edgeLength;
edgeLengthSum += edgeLength;
}
find_neighbor_count ++;
} else if (prim->lines[line_idx][1] == point_idx) {
neighborVertID["neighbor_" + std::to_string(find_neighbor_count)] = prim->lines[line_idx][0];
if (useEdgeLength) {
float edgeLength = length(prim->verts[prim->lines[line_idx][0]] - prim->verts[point_idx]);
neighborEdgeLength["edgeweight_" + std::to_string(find_neighbor_count)] = edgeLength;
edgeLengthSum += edgeLength;
}
find_neighbor_count++;
}
if (find_neighbor_count >= 7)
flag = true;
}
} else if (prim_type == "tri") {
std::vector<int> pointNeighborSign(prim->verts.size());
std::fill(pointNeighborSign.begin(), pointNeighborSign.end(), 0);
//========================================
// if (point_idx == 50000) {
// QueryPerformanceFrequency(&tc_1);
// QueryPerformanceCounter(&t1_1);
// }
//========================================
#pragma omp parallel for
for (size_t tri_idx = 0; tri_idx < prim->tris.size(); tri_idx++) {
auto const &ind = prim->tris[tri_idx];
if (ind[0] == point_idx) {
pointNeighborSign[ind[1]] = 1;
pointNeighborSign[ind[2]] = 1;
} else if (ind[1] == point_idx) {
pointNeighborSign[ind[0]] = 1;
pointNeighborSign[ind[2]] = 1;
} else if (ind[2] == point_idx) {
pointNeighborSign[ind[0]] = 1;
pointNeighborSign[ind[1]] = 1;
}
}
#pragma omp parallel for shared(flag)
for (int i = 0; i < prim->verts.size(); i++) {
if(flag) continue;
if (pointNeighborSign[i]) {
neighborVertID["neighbor_" + std::to_string(find_neighbor_count)] = i;
if (useEdgeLength) {
float edgeLength = length(prim->verts[i] - prim->verts[point_idx]);
neighborEdgeLength["edgeweight_" + std::to_string(find_neighbor_count)] = edgeLength;
edgeLengthSum += edgeLength;
}
find_neighbor_count ++;
}
if (find_neighbor_count >= 7)
flag = true;
}
//========================================
// if (point_idx == 50000) {
// QueryPerformanceCounter(&t2_1);
// double time_1 = (double)(t2_1.QuadPart - t1_1.QuadPart)/(double)tc_1.QuadPart;
// printf("time_1 = %f s\n", time_1);
// }
//========================================
}
neighbor_0[point_idx] = neighborVertID["neighbor_0"];
neighbor_1[point_idx] = neighborVertID["neighbor_1"];
neighbor_2[point_idx] = neighborVertID["neighbor_2"];
neighbor_3[point_idx] = neighborVertID["neighbor_3"];
neighbor_4[point_idx] = neighborVertID["neighbor_4"];
neighbor_5[point_idx] = neighborVertID["neighbor_5"];
neighbor_6[point_idx] = neighborVertID["neighbor_6"];
neighbor_7[point_idx] = neighborVertID["neighbor_7"];
if (useEdgeLength) {
float min_length = (edgeLengthSum / (float)(find_neighbor_count)) * 0.001f;
float sum = 0;
#pragma omp parallel for
for (int i = 0; i < find_neighbor_count; i++)
{
float length = neighborEdgeLength["edgeweight_" + std::to_string(i)];
if ( length > min_length )
neighborEdgeLength["edgeweight_" + std::to_string(i)] = 1.0 / length;
else // 基本重合的点,不考虑其影响,权重打到 0
neighborEdgeLength["edgeweight_" + std::to_string(i)] = 0;
sum += neighborEdgeLength["edgeweight_" + std::to_string(i)]; // 累计总权重
}
if ( sum > 0 )
{
#pragma omp parallel for
for (int i = 0; i < find_neighbor_count; ++i)
{
neighborEdgeLength["edgeweight_" + std::to_string(i)] /= sum; // 权重归一化
}
}
edgeweight_0[point_idx] = neighborEdgeLength["edgeweight_0"];
edgeweight_1[point_idx] = neighborEdgeLength["edgeweight_1"];
edgeweight_2[point_idx] = neighborEdgeLength["edgeweight_2"];
edgeweight_3[point_idx] = neighborEdgeLength["edgeweight_3"];
edgeweight_4[point_idx] = neighborEdgeLength["edgeweight_4"];
edgeweight_5[point_idx] = neighborEdgeLength["edgeweight_5"];
edgeweight_6[point_idx] = neighborEdgeLength["edgeweight_6"];
edgeweight_7[point_idx] = neighborEdgeLength["edgeweight_7"];
}
}
// QueryPerformanceCounter(&t2_0);
// double time_0 = (double)(t2_0.QuadPart - t1_0.QuadPart)/(double)tc_0.QuadPart;
// printf("time_0 = %f s\n", time_0);
// //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// QueryPerformanceFrequency(&tc_2);
// QueryPerformanceCounter(&t1_2);
// 平滑属性计算
std::visit(
[&](auto ty) {
using T = decltype(ty);
auto &data = prim->verts.attr<T>(attr_name);
auto &data_temp = prim->verts.add_attr<T>("_data_temp");
std::fill(data_temp.begin(), data_temp.end(), T(0));
for (int loop = 0; loop < iterations; loop++) {
#pragma omp parallel for
// data => data_temp
for (size_t i = 0; i < prim->verts.size(); i++) {
std::vector<int> neighborIDs(8);
neighborIDs[0] = neighbor_0[i];
neighborIDs[1] = neighbor_1[i];
neighborIDs[2] = neighbor_2[i];
neighborIDs[3] = neighbor_3[i];
neighborIDs[4] = neighbor_4[i];
neighborIDs[5] = neighbor_5[i];
neighborIDs[6] = neighbor_6[i];
neighborIDs[7] = neighbor_7[i];
std::vector<T> neighborValues(8);
for (int i = 0; i < neighborIDs.size(); i++) {
if (neighborIDs[i] != -1)
neighborValues[i] = data[neighborIDs[i]];
}
std::vector<float> neighborEdgeWeights(8);
neighborEdgeWeights[0] = edgeweight_0[i];
neighborEdgeWeights[1] = edgeweight_1[i];
neighborEdgeWeights[2] = edgeweight_2[i];
neighborEdgeWeights[3] = edgeweight_3[i];
neighborEdgeWeights[4] = edgeweight_4[i];
neighborEdgeWeights[5] = edgeweight_5[i];
neighborEdgeWeights[6] = edgeweight_6[i];
neighborEdgeWeights[7] = edgeweight_7[i];
smooth(neighborIDs, neighborValues, neighborEdgeWeights, useEdgeLength, data[i], weight[i], lambda, data_temp[i]);
}
#pragma omp parallel for
// data_temp => data
for (size_t i = 0; i < prim->verts.size(); i++) {
std::vector<int> neighborIDs(8);
neighborIDs[0] = neighbor_0[i];
neighborIDs[1] = neighbor_1[i];
neighborIDs[2] = neighbor_2[i];
neighborIDs[3] = neighbor_3[i];
neighborIDs[4] = neighbor_4[i];
neighborIDs[5] = neighbor_5[i];
neighborIDs[6] = neighbor_6[i];
neighborIDs[7] = neighbor_7[i];
std::vector<T> neighborValues(8);
for(int i = 0; i < neighborIDs.size(); i++)
{
if (neighborIDs[i] != -1)
neighborValues[i] = data_temp[neighborIDs[i]];
}
std::vector<float> neighborEdgeWeights(8);
neighborEdgeWeights[0] = edgeweight_0[i];
neighborEdgeWeights[1] = edgeweight_1[i];
neighborEdgeWeights[2] = edgeweight_2[i];
neighborEdgeWeights[3] = edgeweight_3[i];
neighborEdgeWeights[4] = edgeweight_4[i];
neighborEdgeWeights[5] = edgeweight_5[i];
neighborEdgeWeights[6] = edgeweight_6[i];
neighborEdgeWeights[7] = edgeweight_7[i];
smooth(neighborIDs, neighborValues, neighborEdgeWeights, useEdgeLength, data_temp[i], weight[i], mu, data[i]);
}
}
prim->verts.erase_attr("_data_temp");
},
enum_variant<std::variant<float, vec3f>>(
array_index({"float", "vec3f"}, attr_type)));
// QueryPerformanceCounter(&t2_2);
// double time_2 = (double)(t2_2.QuadPart - t1_2.QuadPart)/(double)tc_2.QuadPart;
// printf("time_2 = %f s\n", time_2);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prim->verts.erase_attr("_neighbor_0");
prim->verts.erase_attr("_neighbor_1");
prim->verts.erase_attr("_neighbor_2");
prim->verts.erase_attr("_neighbor_3");
prim->verts.erase_attr("_neighbor_4");
prim->verts.erase_attr("_neighbor_5");
prim->verts.erase_attr("_neighbor_6");
prim->verts.erase_attr("_neighbor_7");
prim->verts.erase_attr("_edgeweight_0");
prim->verts.erase_attr("_edgeweight_1");
prim->verts.erase_attr("_edgeweight_2");
prim->verts.erase_attr("_edgeweight_3");
prim->verts.erase_attr("_edgeweight_4");
prim->verts.erase_attr("_edgeweight_5");
prim->verts.erase_attr("_edgeweight_6");
prim->verts.erase_attr("_edgeweight_7");
set_output("prim", std::move(prim));
}
};
ZENDEFNODE(PrimAttribBlur,
{/* inputs: */ {
"prim",
{"enum line tri", "primType", "tri"},
// {"string", "group", "mask"},
{"string", "attributes", "ratio"},
{"enum float vec3f ", "attributesType", "float"},
{"bool", "useEdgeLengthWeight", "false"},
{"int", "blurringIterations", "0"},
{"enum laplacian VolumePreserving custom", "mode", "laplacian"},
{"float", "stepSize", "0.683"},
{"float", "cutoffFrequency", "0.1"},
{"float", "evenStepSize", "0.5"},
{"float", "oddStepSize", "0.5"},
{"string", "weightAttributes", "weight"},
}, /* outputs: */ {
"prim",
}, /* params: */ {
}, /* category: */ {
"primCurve",
}});
A signal processing approach to fair surface design
于 2023-06-01 14:49:48 首次发布