7

我正在尝试制作一个立方体,它是不规则的三角形,但实际上是共面的,阴影正确。

这是我目前的结果: 在此处输入图像描述

带线框:

在此处输入图像描述

在我的程序中计算的法线:

在此处输入图像描述

由 meshlabjs.net 计算的法线:

在此处输入图像描述

为立方体使用常规大小的三角形时,照明可以正常工作。如您所见,我正在复制顶点并使用角度加权。

lighting.frag

vec4 scene_ambient = vec4(1, 1, 1, 1.0);

struct material
{
  vec4 ambient;
  vec4 diffuse;
  vec4 specular;
  float shininess;
};

material frontMaterial = material(
  vec4(0.25, 0.25, 0.25, 1.0),
  vec4(0.4, 0.4, 0.4, 1.0),
  vec4(0.774597, 0.774597, 0.774597, 1.0),
  76
);

struct lightSource
{
  vec4 position;
  vec4 diffuse;
  vec4 specular;
  float constantAttenuation, linearAttenuation, quadraticAttenuation;
  float spotCutoff, spotExponent;
  vec3 spotDirection;
};

lightSource light0 = lightSource(
  vec4(0.0,  0.0, 0.0, 1.0),
  vec4(100.0,  100.0,  100.0, 100.0),
  vec4(100.0,  100.0,  100.0, 100.0),
  0.1, 1, 0.01,
  180.0, 0.0,
  vec3(0.0, 0.0, 0.0)
);

vec4 light(lightSource ls, vec3 norm, vec3 deviation, vec3 position)
{
  vec3 viewDirection = normalize(vec3(1.0 * vec4(0, 0, 0, 1.0) - vec4(position, 1)));

  vec3 lightDirection;
  float attenuation;

  //ls.position.xyz = cameraPos;
  ls.position.z += 50;

  if (0.0 == ls.position.w) // directional light?
  {
    attenuation = 1.0; // no attenuation
    lightDirection = normalize(vec3(ls.position));
  } 
  else // point light or spotlight (or other kind of light) 
  {
      vec3 positionToLightSource = vec3(ls.position - vec4(position, 1.0));
    float distance = length(positionToLightSource);
    lightDirection = normalize(positionToLightSource);
    attenuation = 1.0 / (ls.constantAttenuation
      + ls.linearAttenuation * distance
      + ls.quadraticAttenuation * distance * distance);

    if (ls.spotCutoff <= 90.0) // spotlight?
    {
      float clampedCosine = max(0.0, dot(-lightDirection, ls.spotDirection));
      if (clampedCosine < cos(radians(ls.spotCutoff))) // outside of spotlight cone?
      {
        attenuation = 0.0;
        }
      else
        {
        attenuation = attenuation * pow(clampedCosine, ls.spotExponent);   
        }
    }
  }

  vec3 ambientLighting = vec3(scene_ambient) * vec3(frontMaterial.ambient);

  vec3 diffuseReflection = attenuation 
    * vec3(ls.diffuse) * vec3(frontMaterial.diffuse)
    * max(0.0, dot(norm, lightDirection));

  vec3 specularReflection;
  if (dot(norm, lightDirection) < 0.0) // light source on the wrong side?
  {
    specularReflection = vec3(0.0, 0.0, 0.0); // no specular reflection
  }
  else // light source on the right side
  {
    specularReflection = attenuation * vec3(ls.specular) * vec3(frontMaterial.specular)
         * pow(max(0.0, dot(reflect(lightDirection, norm), viewDirection)), frontMaterial.shininess);
  }

  return vec4(ambientLighting + diffuseReflection + specularReflection, 1.0);
}

vec4 generateGlobalLighting(vec3 norm, vec3 position)
{
  return light(light0, norm, vec3(2,0,0), position);
}

mainmesh.frag

#version 430
in vec3 f_color;
in vec3 f_normal;
in vec3 f_position;

in float f_opacity;

out vec4 fragColor;

vec4 generateGlobalLighting(vec3 norm, vec3 position);

void main()
{
  vec3 norm = normalize(f_normal);
  vec4 l0 = generateGlobalLighting(norm, f_position);

  fragColor = vec4(f_color, f_opacity) * l0;
}

按照代码为画家生成顶点、法线和面。

m_vertices_buf.resize(m_mesh.num_faces() * 3, 3);
m_normals_buf.resize(m_mesh.num_faces() * 3, 3);
m_faces_buf.resize(m_mesh.num_faces(), 3);

std::map<vertex_descriptor, std::list<Vector3d>> map;
GLDebugging* deb = GLDebugging::getInstance();

auto getAngle = [](Vector3d a, Vector3d b) {
    double angle = 0.0;
    angle = std::atan2(a.cross(b).norm(), a.dot(b));
    return angle;
};

for (const auto& f : m_mesh.faces()) {
    auto f_hh = m_mesh.halfedge(f);
    //auto n = PMP::compute_face_normal(f, m_mesh);

    vertex_descriptor vs[3];
    Vector3d ps[3];

    int i = 0;
    for (const auto& v : m_mesh.vertices_around_face(f_hh)) {
        auto p = m_mesh.point(v);
        ps[i] = Vector3d(p.x(), p.y(), p.z());
        vs[i++] = v;
    }

    auto n = (ps[1] - ps[0]).cross(ps[2] - ps[0]).normalized();

    auto a1 = getAngle((ps[1] - ps[0]).normalized(), (ps[2] - ps[0]).normalized());
    auto a2 = getAngle((ps[2] - ps[1]).normalized(), (ps[0] - ps[1]).normalized());
    auto a3 = getAngle((ps[0] - ps[2]).normalized(), (ps[1] - ps[2]).normalized());

    auto area = PMP::face_area(f, m_mesh);

    map[vs[0]].push_back(n * a1);
    map[vs[1]].push_back(n * a2);
    map[vs[2]].push_back(n * a3);

    auto p = m_mesh.point(vs[0]);
    deb->drawLine(Vector3d(p.x(), p.y(), p.z()), Vector3d(p.x(), p.y(), p.z()) + Vector3d(n.x(), n.y(), n.z()) * 4);

    p = m_mesh.point(vs[1]);
    deb->drawLine(Vector3d(p.x(), p.y(), p.z()), Vector3d(p.x(), p.y(), p.z()) + Vector3d(n.x(), n.y(), n.z()) * 4);

    p = m_mesh.point(vs[2]);
    deb->drawLine(Vector3d(p.x(), p.y(), p.z()), Vector3d(p.x(), p.y(), p.z()) + Vector3d(n.x(), n.y(), n.z()) * 4);
}

int j = 0;
int i = 0;
for (const auto& f : m_mesh.faces()) {
    auto f_hh = m_mesh.halfedge(f);
    for (const auto& v : m_mesh.vertices_around_face(f_hh)) {
        const auto& p = m_mesh.point(v);
        m_vertices_buf.row(i) = RowVector3d(p.x(), p.y(), p.z());

        Vector3d n(0, 0, 0);

        //auto n = PMP::compute_face_normal(f, m_mesh);
        Vector3d norm = Vector3d(n.x(), n.y(), n.z());

        for (auto val : map[v]) {
            norm += val;
        }

        norm.normalize();

        deb->drawLine(Vector3d(p.x(), p.y(), p.z()), Vector3d(p.x(), p.y(), p.z()) + Vector3d(norm.x(), norm.y(), norm.z()) * 3,
            Vector3d(1.0, 0, 0));

        m_normals_buf.row(i++) = RowVector3d(norm.x(), norm.y(), norm.z());
    }

    m_faces_buf.row(j++) = RowVector3i(i - 3, i - 2, i - 1);
}

遵循画家代码:

m_vertexAttrLoc = program.attributeLocation("v_vertex");
m_colorAttrLoc = program.attributeLocation("v_color");
m_normalAttrLoc = program.attributeLocation("v_normal");

m_mvMatrixLoc = program.uniformLocation("v_modelViewMatrix");
m_projMatrixLoc = program.uniformLocation("v_projectionMatrix");
m_normalMatrixLoc = program.uniformLocation("v_normalMatrix");
//m_relativePosLoc = program.uniformLocation("v_relativePos");
m_opacityLoc = program.uniformLocation("v_opacity");
m_colorMaskLoc = program.uniformLocation("v_colorMask");

//bool for unmapping depth color
m_useDepthMap = program.uniformLocation("v_useDepthMap");
program.setUniformValue(m_mvMatrixLoc, modelView);

//uniform used for Color map to regular model switch
program.setUniformValue(m_useDepthMap, (m_showColorMap &&
    (m_showProblemAreas || m_showPrepMap || m_showDepthMap || m_showMockupMap)));

QMatrix3x3 normalMatrix = modelView.normalMatrix();
program.setUniformValue(m_normalMatrixLoc, normalMatrix);
program.setUniformValue(m_projMatrixLoc, projection);

//program.setUniformValue(m_relativePosLoc, m_relativePos);
program.setUniformValue(m_opacityLoc, m_opacity);
program.setUniformValue(m_colorMaskLoc, m_colorMask);

glEnableVertexAttribArray(m_vertexAttrLoc);
m_vertices.bind();
glVertexAttribPointer(m_vertexAttrLoc, 3, GL_DOUBLE, false, 3 * sizeof(GLdouble), NULL);
m_vertices.release();

glEnableVertexAttribArray(m_normalAttrLoc);
m_normals.bind();
glVertexAttribPointer(m_normalAttrLoc, 3, GL_DOUBLE, false, 0, NULL);
m_normals.release();

glEnableVertexAttribArray(m_colorAttrLoc);

if (m_showProblemAreas) {
    m_problemColorMap.bind();
    glVertexAttribPointer(m_colorAttrLoc, 3, GL_DOUBLE, false, 0, NULL);
    m_problemColorMap.release();
}
else if (m_showPrepMap) {
    m_prepColorMap.bind();
    glVertexAttribPointer(m_colorAttrLoc, 3, GL_DOUBLE, false, 0, NULL);
    m_prepColorMap.release();
}
else if (m_showMockupMap) {
    m_mokupColorMap.bind();
    glVertexAttribPointer(m_colorAttrLoc, 3, GL_DOUBLE, false, 0, NULL);
    m_mokupColorMap.release();
}
else {
    //m_colors.bind();
    //glVertexAttribPointer(m_colorAttrLoc, 3, GL_DOUBLE, false, 0, NULL);
    //m_colors.release();
}

m_indices.bind();
glDrawElements(GL_TRIANGLES, m_indices.size() / sizeof(int), GL_UNSIGNED_INT, NULL);
m_indices.release();


glDisableVertexAttribArray(m_vertexAttrLoc);
glDisableVertexAttribArray(m_normalAttrLoc);
glDisableVertexAttribArray(m_colorAttrLoc);

编辑:抱歉不够清楚。立方体只是一个例子。我的要求是阴影适用于任何类型的网格。那些边缘非常锋利的,以及那些非常有机的(如人类或动物)。

4

3 回答 3

5

您的问题中的图像“在我的程序中计算的法线”清楚地解释了这个问题。立方体角和边缘的法向量不垂直于面:

为了在平面上进行适当的镜面反射,法向量必须垂直于立方体的侧面。

来自具有 6 个分量(x、y、z、nx、ny、nz)的元组的顶点坐标及其法线向量。立方体边缘上的顶点坐标与立方体的 2 个边和 2 个(面)法向量相邻。立方体 8 个角上的 8 个顶点坐标分别与 3 个边(3 个法向量)相邻。

要使用面法线向量(垂直于一侧)定义顶点属性,您必须定义具有相同顶点坐标但不同法线向量的多个元组。您必须使用不同的属性元组在立方体的不同侧面形成三角形图元。

例如,如果您定义了一个立方体,其左、前、下坐标为 (-1, -1, -1),右、后、上坐标为 (1, 1, 1),则顶点坐标 (-1 , -1, -1) 与立方体的左、前和下侧相邻:

         x  y  z   nx ny nz
left:   -1 -1 -1   -1  0  0
front:  -1 -1 -1    0 -1  0
bottom: -1 -1 -1    0  0 -1

使用left属性元组形成左侧的三角形图元,front形成前面bottom的三角形,底部的三角形。


一般来说,你必须决定你想要什么。没有适用于所有网格的通用方法。
要么你有一个细粒度的网格,你想要一个光滑的外观(例如一个球体)。在这种情况下,您的方法很好,它将在基元之间的边缘上生成平滑的光过渡。
或者你有一个像立方体一样有硬边的网格。在这种情况下,您必须“复制”顶点。如果 2 个(甚至更多)三角形共享一个顶点坐标,但面法向量不​​同,则必须为顶点坐标和面法向量的所有组合创建一个单独的元组。

对于一般的“平滑”解决方案,您必须根据周围的几何形状对位于平面中间的顶点坐标的法线向量进行插值。这意味着如果一堆三角形基元形成一个平面,则必须根据平面上的位置计算顶点的所有法线向量。在质心处,法线向量等于面法线向量。对于所有其他点,法线向量必须与周围面的法线向量进行插值。

无论如何,这似乎是一个XY 问题。为什么平面中间某处有一个“顶点”?可能飞机是镶嵌的。但是,如果计划是细分的,为什么在细分过程中法向量也没有被插值呢?

于 2020-02-18T15:47:43.370 回答
3

正如其他答案中提到的,问题是您的网格法线。计算平均法线,就像你现在正在做的那样,是你想要为一个像球体这样的光滑物体做的事情。
cgal有一个函数CGAL::Polygon_mesh_processing::compute_vertex_normal
对于一个立方体,你想要的是垂直于面的法线
cgal也有一个函数CGAL::Polygon_mesh_processing::compute_face_normal


要调试法线,您只需fragColor = vec4(norm,1);mainmesh.frag. 这里左边的立方体有平均(平滑)法线,右边有面(平面)法线:阴影它们看起来像这样:
在此处输入图像描述

在此处输入图像描述


着色必须适用于任何类型的网格(立方体或任何有机网格)

为此,您可以使用per_corner_normals 之类的东西:

实现一个简单的方案,该方案将角法线计算为入射在相应顶点上的面的法线平均值,其偏差不超过指定的二面角(例如 20°)

这就是它在 1°、20°、100° 的角度下的样子: 在此处输入图像描述

于 2020-02-20T05:22:26.683 回答
3

在您的图像中,我们可以看到内部三角形(在立方体边缘没有点的三角形,位于左上角)具有均匀的颜色。

我的解释是,在立方体的边缘/角上有点的三角形共享相同的顶点,然后共享相同的法线以及一些法线的平均方式。所以它不垂直于面。

要对此进行调试,您应该创建一个简单的立方体几何体,它有 6 个面,每个面有 2 个三角形。因此,它制作了 12 个三角形。

两种选择:

  • 如果几何中有 8 个顶点,则角在不同面的三角形之间共享,问题来自几何生成器。
  • 如果几何中有 6×4=24 个顶点,那么真相就在别处。
于 2020-02-04T09:40:37.240 回答