2010-03-03 6 views

risposta

21

Sì. Bene, un po '. Le mappe normali possono essere fatte con precisione dalle mappe dell'altezza. In generale, puoi anche ottenere una trama regolare e ottenere risultati decenti. Tieni presente che esistono altri metodi per creare una mappa normale, come prendere un modello ad alta risoluzione, renderlo a bassa risoluzione, quindi eseguire il ray casting per vedere quale dovrebbe essere normale per il modello a bassa risoluzione per simulare quello più alto.

Per l'altezza della mappa sulla mappa normale, è possibile utilizzare Sobel Operator. Questo operatore può essere eseguito nella direzione x, indicando il componente x del normale e quindi la direzione y, indicando il componente y. Puoi calcolare z con 1.0/strength dove la forza è l'enfasi o "profondità" della mappa normale. Quindi, prendi quella x, yez, gettali in un vettore, normalizzalo e hai il tuo normale in quel punto. Codificalo nel pixel e il gioco è fatto.

Ecco alcuni incompleta-vecchio codice che illustra questo:

// 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

Una cosa che dovrebbe essere aggiunta a questa risposta è che il normale finale deve essere convertito nello spazio 0..1 (è nello spazio -1.1): color = color * 0.5 + 0.5; –

1

Non penso che le mappe normali siano generate da una trama. sono generati da un modello.

proprio come texturing consente di definire complessi dettagli a colori con poligoni minimi (in contrasto con appena usando milioni di stratagemmi e solo i colori dei vertici per definire il colore sulla vostra rete)

Una mappa normale consente di definire complessa dettaglio normale con polys minimale.

Credo che le mappe normali siano generalmente generate da una mesh con una risoluzione più alta e quindi vengano utilizzate con una mesh a bassa risoluzione.

Sono sicuro che gli strumenti 3D, come 3ds max o maya, oltre a strumenti più specifici, faranno questo per voi. a differenza delle trame, non penso che di solito siano fatte a mano.

ma sono generati dalla trama, non dalla trama.

+2

Se si considera che la struttura rappresenta un modello, in quanto il livello di intensità per ciascun pixel costituisce un'altezza e che questa serie di punti altezze potrebbe essere interpretata come una maglia, normali può essere generato da una trama. Il metodo è descritto nella risposta in aumento. –

2

Ci sono probabilmente molti modi per generare una mappa normale, ma come altri hanno detto, lo si può fare da un'altezza Map e pacchetti 3D come XSI/3dsmax/Blender/ognuno di essi può emetterne uno come immagine.

È possibile quindi l'output e l'immagine RGB con il plug-in Nvidia per Photoshop, un algoritmo per convertirlo o si potrebbe essere in grado di produrlo direttamente da quei pacchetti 3d con plug-in di terze parti.

Tenere presente che in alcuni casi potrebbe essere necessario invertire i canali (R, G o B) dalla mappa normale generata.

Ecco alcune risorse dei collegamenti con esempi e più completa spiegazione:

  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

Suggerisco a partire da OpenCV, grazie alla sua ricchezza di algoritmi.Ecco uno che ho scritto che sfuma in modo iterativo la mappa normale e pesa quelli per il valore complessivo, essenzialmente creando più di una mappa topologica.

#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; 
} 
Problemi correlati