2010-03-03 25 views

Trả lời

21

Có. Vâng, loại. Bản đồ bình thường có thể được tạo chính xác từ bản đồ độ cao. Nói chung, bạn cũng có thể đặt một kết cấu thông thường thông qua và có được kết quả khá là tốt. Hãy nhớ rằng có những phương pháp khác để tạo một bản đồ bình thường, chẳng hạn như lấy một mô hình có độ phân giải cao, làm cho độ phân giải thấp, sau đó thực hiện phép chiếu tia để xem mức độ bình thường của mô hình có độ phân giải thấp là gì.

Để có bản đồ độ cao cho bản đồ bình thường, bạn có thể sử dụng Sobel Operator. Toán tử này có thể chạy theo hướng x, cho bạn biết thành phần x của bình thường, và sau đó là hướng y, cho bạn biết thành phần y. Bạn có thể tính z với 1.0/strength, nơi sức mạnh là sự nhấn mạnh hoặc "độ sâu" của bản đồ bình thường. Sau đó, lấy x, y, và z đó, ném chúng vào một vectơ, bình thường hóa nó, và bạn có bình thường tại điểm đó. Mã hóa nó vào pixel và bạn đã hoàn tất.

Dưới đây là một số không đầy đủ mã cũ mà chứng tỏ điều này:

// 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<double>(pPixel.red); 
    const double g = static_cast<double>(pPixel.green); 
    const double b = static_cast<double>(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<int>(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(v.x), map_component(v.y), map_component(v.z)); 
     } 
    } 

    return result; 
} 
+0

Một điều cần được thêm vào câu trả lời này là bình thường cuối cùng phải được chuyển đổi thành không gian 0..1 (trong không gian -1.1.1): color = color * 0.5 + 0.5; –

1

Tôi không nghĩ rằng các bản đồ thông thường được tạo từ kết cấu. chúng được tạo ra từ một mô hình.

cũng giống như texturing cho phép bạn xác định chi tiết màu sắc phức tạp với polys tối thiểu (như trái ngược với chỉ sử dụng hàng triệu thủ đoạn và chỉ màu sắc đỉnh để xác định màu sắc trên lưới của bạn)

Một bản đồ bình thường cho phép bạn xác định phức tạp chi tiết bình thường với polys tối thiểu.

Tôi tin rằng các bản đồ bình thường thường được tạo ra từ lưới có độ phân giải cao hơn, và sau đó được sử dụng với lưới có độ phân giải thấp.

Tôi chắc chắn các công cụ 3D, chẳng hạn như 3ds max hoặc maya, cũng như các công cụ cụ thể hơn sẽ làm điều này cho bạn. không giống như kết cấu, tôi không nghĩ rằng chúng thường được thực hiện bằng tay.

nhưng chúng được tạo từ lưới chứ không phải kết cấu.

+2

Nếu bạn cho rằng kết cấu đại diện cho một mô hình, ở đó mức độ cường độ cho mỗi pixel tạo thành chiều cao và chuỗi điểm cao này có thể được hiểu là lưới. Phương pháp được nêu trong câu trả lời được bình chọn. –

2

Có lẽ nhiều cách để tạo ra một bản đồ bình thường, nhưng cũng giống như những người khác nói, bạn có thể làm điều đó từ một Height Bản đồ, và các gói 3d như XSI/3dsmax/Blender/bất kỳ trong số chúng có thể xuất ra một hình ảnh cho bạn. Sau đó, bạn có thể xuất ra và hình ảnh RGB với plugin Nvidia cho photoshop, một thuật toán để chuyển đổi nó hoặc bạn có thể xuất trực tiếp từ các gói 3D đó bằng các plugin của bên thứ 3.

Lưu ý rằng trong một số trường hợp, bạn có thể cần đảo ngược kênh (R, G hoặc B) từ bản đồ bình thường được tạo.

Dưới đây là một số tài nguyên liên kết với các ví dụ và giải thích đầy đủ hơn:

  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
0

Tôi đề nghị bắt đầu với OpenCV, do sự phong phú của nó trong thuật toán.Đây là một trong những tôi đã viết rằng iteratively blurs bản đồ bình thường và trọng lượng những người với giá trị tổng thể, về cơ bản tạo ra nhiều hơn của một bản đồ topo.

#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; y<bwTexture.rows; y++) { 
     const uchar *sobelXPtr = ROW_PTR(sobelX, y); 
     const uchar *sobelYPtr = ROW_PTR(sobelY, y); 
     uchar *sobelZPtr = ROW_PTR(sobelZ, y); 

     for(int x=0; x<bwTexture.cols; x++) { 
      double Gx = double(sobelXPtr[x])/255.0; 
      double Gy = double(sobelYPtr[x])/255.0; 

      double Gz = pStrength * sqrt(Gx * Gx + Gy * Gy); 

      uchar value = uchar(Gz * 255.0); 

      sobelZPtr[x] = value; 
     } 
    } 

    std::vector<cv::Mat>planes; 

    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; 
} 
Các vấn đề liên quan