| 32 | } |
| 33 | |
| 34 | QImage NormalmapGenerator::calculateNormalmap(const QImage& input, Kernel kernel, double strength, bool invert, bool tileable, |
| 35 | bool keepLargeDetail, int largeDetailScale, double largeDetailHeight) { |
| 36 | this->tileable = tileable; |
| 37 | |
| 38 | this->intensity = IntensityMap(input, mode, useRed, useGreen, useBlue, useAlpha); |
| 39 | if(!invert) { |
| 40 | // The default "non-inverted" normalmap looks wrong in renderers, |
| 41 | // so I use inversion by default |
| 42 | intensity.invert(); |
| 43 | } |
| 44 | |
| 45 | QImage result(input.width(), input.height(), QImage::Format_ARGB32); |
| 46 | |
| 47 | // optimization |
| 48 | double strengthInv = 1.0 / strength; |
| 49 | |
| 50 | #pragma omp parallel for // OpenMP |
| 51 | //code from http://stackoverflow.com/a/2368794 |
| 52 | for(int y = 0; y < input.height(); y++) { |
| 53 | QRgb *scanline = (QRgb*) result.scanLine(y); |
| 54 | |
| 55 | for(int x = 0; x < input.width(); x++) { |
| 56 | |
| 57 | const double topLeft = intensity.at(handleEdges(x - 1, input.width()), handleEdges(y - 1, input.height())); |
| 58 | const double top = intensity.at(handleEdges(x - 1, input.width()), handleEdges(y, input.height())); |
| 59 | const double topRight = intensity.at(handleEdges(x - 1, input.width()), handleEdges(y + 1, input.height())); |
| 60 | const double right = intensity.at(handleEdges(x, input.width()), handleEdges(y + 1, input.height())); |
| 61 | const double bottomRight = intensity.at(handleEdges(x + 1, input.width()), handleEdges(y + 1, input.height())); |
| 62 | const double bottom = intensity.at(handleEdges(x + 1, input.width()), handleEdges(y, input.height())); |
| 63 | const double bottomLeft = intensity.at(handleEdges(x + 1, input.width()), handleEdges(y - 1, input.height())); |
| 64 | const double left = intensity.at(handleEdges(x, input.width()), handleEdges(y - 1, input.height())); |
| 65 | |
| 66 | const double convolution_kernel[3][3] = {{topLeft, top, topRight}, |
| 67 | {left, 0.0, right}, |
| 68 | {bottomLeft, bottom, bottomRight}}; |
| 69 | |
| 70 | QVector3D normal(0, 0, 0); |
| 71 | |
| 72 | if(kernel == SOBEL) |
| 73 | normal = sobel(convolution_kernel, strengthInv); |
| 74 | else if(kernel == PREWITT) |
| 75 | normal = prewitt(convolution_kernel, strengthInv); |
| 76 | |
| 77 | scanline[x] = qRgb(mapComponent(normal.x()), mapComponent(normal.y()), mapComponent(normal.z())); |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | if(keepLargeDetail) { |
| 82 | //generate a second normalmap from a downscaled input image, then mix both normalmaps |
| 83 | |
| 84 | int largeDetailMapWidth = (int) (((double)input.width() / 100.0) * largeDetailScale); |
| 85 | int largeDetailMapHeight = (int) (((double)input.height() / 100.0) * largeDetailScale); |
| 86 | |
| 87 | //create downscaled version of input |
| 88 | QImage inputScaled = input.scaled(largeDetailMapWidth, largeDetailMapHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); |
| 89 | //compute downscaled normalmap |
| 90 | QImage largeDetailMap = calculateNormalmap(inputScaled, kernel, largeDetailHeight, invert, tileable, false, 0, 0.0); |
| 91 | //scale map up |
no test coverage detected