可以从纹理生成法线贴图吗?

如果我有纹理,那么可以为这个纹理生成法线贴图,那么它可以用于凹凸贴图吗?

或者通常如何制作法线贴图?

是。 好吧,有点。 可以从高度图准确地制作法线贴图。 一般来说,你也可以通过常规纹理,并获得不错的结果。 请记住,还有其他制作法线贴图的方法,例如采用高分辨率模型,使其分辨率较低,然后进行光线投射,以查看低分辨率模型模拟较高分辨率模型的正常情况。

对于高度贴图到法线贴图,您可以使用Sobel算子 。 此运算符可以在x方向上运行,告诉您正常的x分量,然后是y方向,告诉您y分量。 你可以用1.0 / strength计算z,其中强度是法线贴图的重点或“深度”。 然后,取出x,y和z,将它们放入一个向量中,对其进行标准化,然后你就可以正常了。 将其编码到像素中即可完成。

这是一些较旧的不完整代码,用于演示:

 // pretend types, something like this struct pixel { uint8_t red; uint8_t green; uint8_t blue; }; struct vector3d; // a 3-vector with doubles struct texture; // a 2d array of pixels // determine intensity of pixel, from 0 - 1 const double intensity(const pixel& pPixel) { const double r = static_cast(pPixel.red); const double g = static_cast(pPixel.green); const double b = static_cast(pPixel.blue); const double average = (r + g + b) / 3.0; return average / 255.0; } const int clamp(int pX, int pMax) { if (pX > pMax) { return pMax; } else if (pX < 0) { return 0; } else { return pX; } } // transform -1 - 1 to 0 - 255 const uint8_t map_component(double pX) { return (pX + 1.0) * (255.0 / 2.0); } texture normal_from_height(const texture& pTexture, double pStrength = 2.0) { // assume square texture, not necessarily true in real code texture result(pTexture.size(), pTexture.size()); const int textureSize = static_cast(pTexture.size()); for (size_t row = 0; row < textureSize; ++row) { for (size_t column = 0; column < textureSize; ++column) { // surrounding pixels const pixel topLeft = pTexture(clamp(row - 1, textureSize), clamp(column - 1, textureSize)); const pixel top = pTexture(clamp(row - 1, textureSize), clamp(column, textureSize)); const pixel topRight = pTexture(clamp(row - 1, textureSize), clamp(column + 1, textureSize)); const pixel right = pTexture(clamp(row, textureSize), clamp(column + 1, textureSize)); const pixel bottomRight = pTexture(clamp(row + 1, textureSize), clamp(column + 1, textureSize)); const pixel bottom = pTexture(clamp(row + 1, textureSize), clamp(column, textureSize)); const pixel bottomLeft = pTexture(clamp(row + 1, textureSize), clamp(column - 1, textureSize)); const pixel left = pTexture(clamp(row, textureSize), clamp(column - 1, textureSize)); // their intensities const double tl = intensity(topLeft); const double t = intensity(top); const double tr = intensity(topRight); const double r = intensity(right); const double br = intensity(bottomRight); const double b = intensity(bottom); const double bl = intensity(bottomLeft); const double l = intensity(left); // sobel filter const double dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl); const double dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr); const double dZ = 1.0 / pStrength; math::vector3d v(dX, dY, dZ); v.normalize(); // convert to rgb result(row, column) = pixel(map_component(vx), map_component(vy), map_component(vz)); } } return result; } 

可能有很多方法可以生成法线贴图,但是像其他人说的那样,你可以从高度贴图中做到,像XSI / 3dsmax / Blender这样的3d包可以为你输出一个作为图像。

然后,您可以使用用于photoshop的Nvidia插件输出RGB图像,这是一种转换它的算法,或者您可以直接从带有第三方插件的3d包输出它。

请注意,在某些情况下,您可能需要从生成的法线贴图中反转通道(R,G或B)。

这里有一些资源链接示例和更完整的解释:

  1. http://developer.nvidia.com/object/photoshop_dds_plugins.html
  2. http://en.wikipedia.org/wiki/Normal_mapping
  3. http://www.vrgeo.org/fileadmin/VRGeo/Bilder/VRGeo_Papers/jgt2002normalmaps.pdf

我不认为从纹理生成法线贴图。 它们是从模型生成的。

正如纹理允许您使用最小的多边形定义复杂的颜色细节(而不是仅使用数百万个伎俩而只是使用顶点颜色来定义网格上的颜色)

法线贴图允许您使用最小的多边形定义复杂的法线细节。

我相信法线贴图通常是从较高的res网格生成的,然后与低res网格一起使用。

我相信3D工具,例如3ds max或maya,以及更具体的工具都可以帮到你。 与纹理不同,我不认为它们通常是手工完成的。

但它们是从网格生成的,而不是纹理生成的。

我建议从OpenCV开始,因为它具有丰富的算法。 这是我写的一个,迭代地模糊法线贴图并将其加权到整体值,实质上创建了更多的拓扑贴图。

 #define ROW_PTR(img, y) ((uchar*)((img).data + (img).step * y)) cv::Mat normalMap(const cv::Mat& bwTexture, double pStrength) { // assume square texture, not necessarily true in real code int scale = 1.0; int delta = 127; cv::Mat sobelZ, sobelX, sobelY; cv::Sobel(bwTexture, sobelX, CV_8U, 1, 0, 13, scale, delta, cv::BORDER_DEFAULT); cv::Sobel(bwTexture, sobelY, CV_8U, 0, 1, 13, scale, delta, cv::BORDER_DEFAULT); sobelZ = cv::Mat(bwTexture.rows, bwTexture.cols, CV_8UC1); for(int y=0; yplanes; planes.push_back(sobelX); planes.push_back(sobelY); planes.push_back(sobelZ); cv::Mat normalMap; cv::merge(planes, normalMap); cv::Mat originalNormalMap = normalMap.clone(); cv::Mat normalMapBlurred; for (int i=0; i<3; i++) { cv::GaussianBlur(normalMap, normalMapBlurred, cv::Size(13, 13), 5, 5); addWeighted(normalMap, 0.4, normalMapBlurred, 0.6, 0, normalMap); } addWeighted(originalNormalMap, 0.3, normalMapBlurred, 0.7, 0, normalMap); return normalMap; }