Nagle said:
This might produce jaggyness. Is it customary to look at more pixels, do some smoothing, or something? There seems to be a standard way to do this, but I can't find it documented.
For 2D erosion sim i did the gradients by summing up all 8 neighbors. It helped to prevent features from becoming axis aligned.
Here some example function, but it's pretty ugly research code.
The main question is how to weight the neighbors. If you think of a box filter of size 2x2 texels, the diagonal neighbors have half the weight than the direct neighbors, so i used that.
void CalcNormalsAndScalarCurvatureFromHeight (int GRID_RES,
std::vector<Vec4> &normalMapW,
const std::vector<Vec4> &heightVelMapR)
{
int GRID_MASK = GRID_RES-1;
//float div = 1.f / float(GRID_RES);
float pipelen = u_PipeLen;
float heightScale = 1.f / u_heightScale;
float div = 1.f / float(GRID_RES);
//bool B0 = config2.GetParamB ("_tempB0", 0);
//bool B1 = config2.GetParamB ("_tempB1", 0);
constexpr int lutUO[8] = { 0, 1, 0, -1, 1, 1, -1, -1};
constexpr int lutVO[8] = {-1, 0, 1, 0, -1, 1, 1, -1};
for(int iV = 0; iV < GRID_RES; iV++)
for(int iU = 0; iU < GRID_RES; iU++)
{
int i = iV * GRID_RES + iU;
bool atBoundary = false;
if (boundary!=Boundary::Tiled)
atBoundary = (iU==0 || iV==0 || iU == GRID_MASK || iV == GRID_MASK);
real h = heightVelMapR[i][TERRAIN] * heightScale;
vec4 normalCurv;
if (1 && !atBoundary)
{
//normal = CalcHeightMapNormalAndCurvature (heightVelR.data(), iU, iV, GRID_RES, TERRAIN, 1/u_heightScale);
real h0 = heightVelMapR[ ((iU-1) & GRID_MASK) + (iV & GRID_MASK) * GRID_RES ][TERRAIN] * heightScale;
real h1 = heightVelMapR[ ((iU+1) & GRID_MASK) + (iV & GRID_MASK) * GRID_RES ][TERRAIN] * heightScale;
real h2 = heightVelMapR[ (iU & GRID_MASK) + ((iV-1) & GRID_MASK) * GRID_RES ][TERRAIN] * heightScale;
real h3 = heightVelMapR[ (iU & GRID_MASK) + ((iV+1) & GRID_MASK) * GRID_RES ][TERRAIN] * heightScale;
real avH = (h0+h1+h2+h3) * 2.f;
real d = 2.f * div;
Vec3 dU (d, h1 - h0, 0);
Vec3 dV (0, h3 - h2, d);
Vec3 norm = normalize(cross(dV, dU));
// diagonal
{
real h0 = heightVelMapR[ ((iU-1) & GRID_MASK) + ((iV-1) & GRID_MASK) * GRID_RES ][TERRAIN] * heightScale;
real h1 = heightVelMapR[ ((iU+1) & GRID_MASK) + ((iV-1) & GRID_MASK) * GRID_RES ][TERRAIN] * heightScale;
real h2 = heightVelMapR[ ((iU-1) & GRID_MASK) + ((iV+1) & GRID_MASK) * GRID_RES ][TERRAIN] * heightScale;
real h3 = heightVelMapR[ ((iU+1) & GRID_MASK) + ((iV+1) & GRID_MASK) * GRID_RES ][TERRAIN] * heightScale;
avH += (h0+h1+h2+h3);
real d = 2.828427f * div;
Vec3 dU ( d, h3 - h0, d);
Vec3 dV ( d, h1 - h2, -d);
norm += normalize(cross(dU, dV)) / 2;
norm = normalize(norm);
}
avH /= 12.f;
normalCurv = Vec4(norm, h - avH);
}
else
{
// 7 0 4
// 3 c 1
// 6 2 5
float count = 0;
float curv = 0;
Vec3 norm (0);
for (int n=0; n<4; n++)
{
int oU1 = lutUO[n+4];
int oV1 = lutVO[n+4];
int aU1 = iU+oU1;
int aV1 = iV+oV1;
if (aU1 < 0 || aV1 < 0 || aU1 >= GRID_RES || aV1 >= GRID_RES)
continue;
int ai1 = aV1 * GRID_RES + aU1;
real h1 = heightVelMapR[ai1][TERRAIN] * heightScale;
Vec3 d1 ( div*oU1, h1 - h, div*oV1);
curv += h1;
count += 1;
int oU0 = lutUO[n];
int oV0 = lutVO[n];
int aU0 = iU+oU0;
int aV0 = iV+oV0;
if (!(aU0 < 0 || aV0 < 0 || aU0 >= GRID_RES || aV0 >= GRID_RES))
{
int ai0 = aV0 * GRID_RES + aU0;
real h0 = heightVelMapR[ai0][TERRAIN] * heightScale;
Vec3 d0 ( div*oU0, h0 - h, div*oV0);
//RenderVector(Vec3((iU+.5f)*div,h,(iV+.5f)*div), d0*.5f, 1,0,0);
//RenderVector(Vec3((iU+.5f)*div,h,(iV+.5f)*div), d1*.5f, 0,1,0);
norm += cross(d1, d0);
curv += h0;
count += 1;
}
oU0 = lutUO[(n+1)&3];
oV0 = lutVO[(n+1)&3];
aU0 = iU+oU0;
aV0 = iV+oV0;
if (!(aU0 < 0 || aV0 < 0 || aU0 >= GRID_RES || aV0 >= GRID_RES))
{
int ai0 = aV0 * GRID_RES + aU0;
real h0 = heightVelMapR[ai0][TERRAIN] * heightScale;
Vec3 d0 ( div*oU0, h0 - h, div*oV0);
//RenderVector(Vec3((iU+.5f)*div,h,(iV+.5f)*div), d0*.4f, 1,0,0.5f);
//RenderVector(Vec3((iU+.5f)*div,h,(iV+.5f)*div), d1*.4f, 0,1,0.5f);
norm += cross(d0, d1);
curv += h0;
count += 1;
}
}
//ImGui::Text("count %f", count);
normalCurv = Vec4(normalize(norm), 0);//h - (curv / count)); // curvature too faulty - would need to reflect
}
normalMapW[i] = normalCurv;
}
}