Antialiased line with C++ Builder VCL - c++

Question: I need to upgrade an old Embarcadero VCL graphic math application by introducing antialiased lines. So, I wrote in C++ the algorithm indicated in the page: https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm.
How to write correctly the function 'plot' to draw the pixel at (x,y) with a brightness 'c', especially on the Embarcadero VCL.
Solution:
This solution has been possible by the contribution of #Spektre (use of a union to mix colors according to some brightness). pC is a canvas pointer, funcColor is the line intended color, and are properties of the Observer class:
//Antialiased line:
void Observer::aaLine(int x0, int y0, int x1, int y1)
{
union {
uint32_t dd;//The color value
uint8_t db[4];//To work on channels: {00.RR.GG.BB}
} c, c0;//Line color, and background color
//Color mixer, with calculations on each channel, because there is no
//Alpha channel with VCL:
auto plot = [&](int X, int Y, float brightness){
c.dd = funcColor;//Line color
c0.dd = pC->Pixels[X][Y];//Background color
//Find coefficients to simulate transparency, where there is not:
//Front color is augmented when background is decreased:
for(int i = 0; i < 3; ++i)
c.db[i] = int(c.db[i] * brightness + c0.db[i] * (1 - brightness));
//Output obtained by conversion:
pC->Pixels[X][Y] = static_cast<TColor>(c.dd);
};
//Wu's algorithm:
//Fractional part of x:
auto fpart = [](double x) { return x - floor(x); };
auto rfpart = [&](double x) { return 1 - fpart(x); };
bool steep = abs(y1 - y0) > abs(x1 - x0);//Means slope > 45 deg.
if(steep) {
std::swap(x0, y0);
std::swap(x1, y1);
}
if( x0 > x1 ) {
std::swap(x0, x1);
std::swap(y0, y1);
}
double dx = x1 - x0, dy = y1 - y0, gradient = (dx == 0. ? 1. : dy/dx) ;
//Handle first endpoint
double xend = x0,
yend = y0 + gradient * (xend - x0),
xgap = rfpart(x0 + 0.5),
xpxl1 = xend, // this will be used in the main loop
ypxl1 = floor(yend);
if( steep ) {
plot(ypxl1, xpxl1, rfpart(yend) * xgap);
plot(ypxl1+1, xpxl1, fpart(yend) * xgap);
}
else {
plot(xpxl1, ypxl1 , rfpart(yend) * xgap);
plot(xpxl1, ypxl1+1, fpart(yend) * xgap);
}
auto intery = yend + gradient; // first y-intersection for the main loop
//Handle second endpoint
xend = round(x1);
yend = y1 + gradient * (xend - x1);
xgap = fpart(x1 + 0.5);
auto xpxl2 = xend, //this will be used in the main loop
ypxl2 = floor(yend);
if( steep ){
plot(ypxl2 , xpxl2, rfpart(yend) * xgap);
plot(ypxl2+1, xpxl2, fpart(yend) * xgap);
//Main loop:
for(double x = xpxl1 + 1 ; x <= xpxl2 - 1 ; x += 1) {
plot(int(intery) , x, rfpart(intery));
plot(int(intery+1), x, fpart(intery));
intery += gradient;
}
}
else {
plot(xpxl2, ypxl2, rfpart(yend) * xgap);
plot(xpxl2, ypxl2+1, fpart(yend) * xgap);
//Main loop:
for(double x = xpxl1 + 1 ; x <= xpxl2 - 1 ; x += 1) {
plot(x, int(intery), rfpart(intery));
plot(x, int(intery+1), fpart(intery));
intery += gradient;
}
}
}//Observer::aaLine.
The source code above is updated, and works for me as a solution.
The image below comes from tests: Blue's are NOT antialiased, and Red's ones are the results from the solution above. I am satisfied with what I want to do.

I think your problem lies in this:
auto plot = [&](double X, double Y, double brighness){
pC->Pixels[X][Y] = brightness; };
If I understand it correctly pC is some target TCanvas ... this has 2 major problems:
pC->Pixels[X][Y] = brightness; will handle brightness as color according to selected mode (so copy,xor,... or whatever) and not as brightness.
I would use form of alpha blending where you take originaly render color (or background) and wanted color of rendered line and mix it with brightness as parameter:
TColor c0=pC->Pixels[X][Y],c0=color of your line;
// here mix colors c = (c0*(1.0-brightness)) + (c1*brightness)
// however you need to do this according to selected pixelformat of you graphic object and color channel wise...
pC->Pixels[X][Y]=c;
Beware VCL transparency does not use alpha parameter its just opaque or not ... For more info about the mixing see similar:
Digital Differential Analyzer with Wu's Algorithm in OpenGL
especially pay attention to the:
union
{
DWORD dd;
BYTE db[4];
} c,c0;
as TColor is 32bit int anyway ...
speed of pC->Pixels[X][Y] in VCL (or any GDI based api) is pitiful at best
in case you handle many pixels you should consider to use ScanLine[Y] from Graphics::TBitmap ... and render to bitmap as backbufer. This usually improve speed from ~1000 to ~10000 times. for more info see:
Graphics rendering in C++

Related

Optimizing raster algorithm in OpenCL 32seconds for a cube on nVidia RTX 3080

I'm new in OpenCl. I wrote an OpenCL software rasterizer to rasterize triangles. Now the time that is used for a cube is 32Seconds, which is too much, I'm testing on nVidia RTX3080 Laptop.
The result is very weird and it's too slow.
Here is the kernel,
___kernel void fragment_shader(__global struct Fragment* fragments, __global struct Triangle_* triangles, int triCount)
{
size_t px = get_global_id(0); // triCount
//size_t py = get_global_id(1); // triCount
int imageWidth = 256;
int imageHeight = 256;
if(px < triCount)
{
float3 v0Raster = (float3)(triangles[px].v[0].pos[0], triangles[px].v[0].pos[1], triangles[px].v[0].pos[2]);
float3 v1Raster = (float3)(triangles[px].v[1].pos[0], triangles[px].v[1].pos[1], triangles[px].v[1].pos[2]);
float3 v2Raster = (float3)(triangles[px].v[2].pos[0], triangles[px].v[2].pos[1], triangles[px].v[2].pos[2]);
float xmin = min3(v0Raster.x, v1Raster.x, v2Raster.x);
float ymin = min3(v0Raster.y, v1Raster.y, v2Raster.y);
float xmax = max3(v0Raster.x, v1Raster.x, v2Raster.x);
float ymax = max3(v0Raster.y, v1Raster.y, v2Raster.y);
float slope = (ymax - ymin) / (xmax - xmin);
// be careful xmin/xmax/ymin/ymax can be negative. Don't cast to uint32_t
unsigned int x0 = max((uint)0, (uint)(floor(xmin)));
unsigned int x1 = min((uint)(imageWidth) - 1, (uint)(floor(xmax)));
unsigned int y0 = max((uint)0, (uint)(floor(ymin)));
unsigned int y1 = min((uint)(imageHeight) - 1, (uint)(floor(ymax)));
float3 v0 = v0Raster;
float3 v1 = v1Raster;
float3 v2 = v2Raster;
float area = edgeFunction(v0Raster, v1Raster, v2Raster);
for (unsigned int y = y0; y <= y1; ++y) {
for (unsigned int x = x0; x <= x1; ++x) {
float3 p = { x + 0.5f, y + 0.5f, 0 };
float w0 = edgeFunction(v1Raster, v2Raster, p);
float w1 = edgeFunction(v2Raster, v0Raster, p);
float w2 = edgeFunction(v0Raster, v1Raster, p);
if (w0 >= 0 && w1 >= 0 && w2 >= 0) {
fragments[y * 256 + x].col[0] = 1.0f;
fragments[y * 256 + x].col[1] = 0;
fragments[y * 256 + x].col[2] = 0;
}
}
}
}
}
The kernel is supposed to run for every triangle, and does box testing and rasterize the pixels.
here is how I invoke it:
global_size[0] = triCount-1;
auto time_start = std::chrono::high_resolution_clock::now();
err = clEnqueueNDRangeKernel(commandQueue, kernel_fragmentShader, 1, NULL, global_size,
NULL, 0, NULL, NULL);
if (err < 0) {
perror("Couldn't enqueue the kernel_fragmentShader");
exit(1);
}
I tried to omit lighting and everything still it takes around 20seconds to render a cube.
This kind of approach is well suited for massively parallel rendering like on GPU. I assume you are doing this on CPU side so the performance is poor as you have no or too small parallelization and no or very little HW support for used operations. On GPU you got SIMD instructions for most of the stuff needed and a lot of is done in HW instead of in code).
To gain speed on CPU side see how to rasterize rotated rectangle this was standard way of SW rendering back in the days before GPUs. The method simply renders edges of convex polygon (or triangle) as lines into 2 buffers (start end poins per horizontal line) and then just fill or interpolate the horizontal lines. This uses much much less operations per pixel.
Your method computes point inside triangle per each pixel of BBOX which meas much more pixels are processed and each pixel need too many complicated operations which kills performance.
On top of this your code is not optimized for example
fragments[y * 256 + x].col[0] = 1.0f;
fragments[y * 256 + x].col[1] = 0;
fragments[y * 256 + x].col[2] = 0;
Why are you computing y * 256 + x 3 times? also I would feel better with (y<<8)+x but nowadays compilers should do it for you. You can also just add 256 to starting address instead of multiplying...
I do not code in OpenCL (IIRC its for computer vision and DIP not for rendering) so I hope you have direct access to fragments[] and not some constrained with additional test which kills performance a lot (similar to putpixel,setpixel,pixel[][],etc. on most gfx apis which can kill performance even up to 10000x times)

Why are my openGL ellipses pointed?

I copied this ellipse code directly from the opengl textbook:
void ellipseMidpoint (int xCenter, int yCenter, int Rx, int Ry)
{
int Rx2 = Rx * Rx;
int Ry2 = Ry * Ry;
int twoRx2 = 2 * Rx2;
int twoRy2 = 2 * Ry2;
int p;
int x = 0;
int y = Ry;
int px = 0;
int py = twoRx2 * y;
//initial points in both quadrants
ellipsePlotPoints (xCenter, yCenter, x, y);
//Region 1
p = round (Ry2 - (Rx2 * Ry) + (0.25 * Rx2));
while (px < py) {
x++;
px += twoRy2;
if (p < 0)
p += Ry2 + px;
else {
y--;
py -= twoRx2;
p += Ry2 + px - py;
}
ellipsePlotPoints (xCenter, yCenter, x, y);
}
//Region 2
p = round (Ry2 * (x+0.5) * (x+0.5) + Rx2 * (y-1) * (y-1) - Rx2 * Ry2);
while (y > 0) {
y--;
py -= twoRx2;
if (p > 0)
p += Rx2 - py;
else {
x++;
px += twoRy2;
p += Rx2 - py + px;
}
ellipsePlotPoints (xCenter, yCenter, x, y);
}
}
void ellipsePlotPoints (int xCenter, int yCenter, int x, int y)
{
setPixel (xCenter + x, yCenter + y);
setPixel (xCenter - x, yCenter + y);
setPixel (xCenter + x, yCenter - y);
setPixel (xCenter - x, yCenter - y);
}
void setPixel (GLint xPos, GLint yPos)
{
glBegin (GL_POINTS);
glVertex2i(xPos, yPos);
glEnd();
}
The smaller ellipses seem to be fine but the larger ones are pointy and sort of flat at the ends.
Any ideas why?
Here is a current screenshot:
I think you're encountering overflow. I played with your code. While I never saw exactly the same "lemon" type shapes from your pictures, things definitely fell apart at large sizes, and it was caused by overflowing the range of the int variables used in the code.
For example, look at one of the first assignments:
int py = twoRx2 * y;
If you substitute, this becomes:
int py = 2 * Rx * Rx * Ry;
If you use a value of 1000 each for Rx and Ry, this is 2,000,000,000. Which is very close to the 2^31 - 1 top of the range of a 32-bit int.
If you want to use this algorithm for larger sizes, you could use 64-bit integer variables. Depending on your system, the type would be long or long long. Or more robustly, int64_t after including <stdint.h>.
Now, if all you want to do is draw an ellipsis with OpenGL, there are much better ways. The Bresenham type algorithms used in your code are ideal if you need to draw a curve pixel by pixel. But OpenGL is a higher level API, which knows how to render more complex primitives than just pixels. For a curve, you will most typically use a connected set of line segments to approximate the curve. OpenGL will then take care of turning those line segments into pixels.
The simplest way to draw an ellipsis is to directly apply the parametric representation. With phi an angle between 0 and PI, and using the naming from your code, the points on the ellipsis are:
x = xCenter + Rx * cos(phi)
y = yCenter + Ry * sin(phi)
You can use an increment for phi that meets your precision requirements, and the code will look something to generate an ellipsis approximated by DIV_COUNT points will look something like this:
float angInc = 2.0f * m_PI / (float)DIV_COUNT;
float ang = 0.0f;
glBegin(GL_LINE_LOOP);
for (int iDiv = 0; iDiv < DIV_COUNT; ++iDiv) {
ang += angInc;
float x = xCenter + Rx * cos(ang);
float y = yCenter + Ry * sin(ang);
glVertex2f(x, y);
glEnd();
If you care about efficiency, you can avoid calculating the trigonometric functions for each point, and apply an incremental rotation to calculate each point from the previous one:
float angInc = 2.0f * M_PI / (float)DIV_COUNT;
float cosInc = cos(angInc);
float sinInc = sin(angInc);
float cosAng = 1.0f;
float sinAng = 0.0f
glBegin(GL_LINE_LOOP);
for (int iDiv = 0; iDiv < DIV_COUNT; ++iDiv) {
float newCosAng = cosInc * cosAng - sinInc * sinAng;
sinAng = sinInc * cosAng + cosInc * sinAng;
cosAng = newCosAng;
float x = xCenter + Rx * cosAng;
float y = yCenter + Ry * sinAng;
glVertex2f(x, y);
glEnd();
This code is of course just for illustrating the math, and to get you started. In reality, you should use current OpenGL rendering methods, which includes vertex buffers, etc.

Bilinear Interpolation, something wrong with my implementation

I am trying to implement a bilinear interpolation function, but for some reason I am getting bad output. I cant seem to figure out what's wrong, any help getting on the right track will be appreciated.
double lerp(double c1, double c2, double v1, double v2, double x)
{
if( (v1==v2) ) return c1;
double inc = ((c2-c1)/(v2 - v1)) * (x - v1);
double val = c1 + inc;
return val;
};
void bilinearInterpolate(int width, int height)
{
// if the current size is the same, do nothing
if(width == GetWidth() && height == GetHeight())
return;
//Create a new image
std::unique_ptr<Image2D> image(new Image2D(width, height));
// x and y ratios
double rx = (double)(GetWidth()) / (double)(image->GetWidth()); // oldWidth / newWidth
double ry = (double)(GetHeight()) / (double)(image->GetHeight()); // oldWidth / newWidth
// loop through destination image
for(int y=0; y<height; ++y)
{
for(int x=0; x<width; ++x)
{
double sx = x * rx;
double sy = y * ry;
uint xl = std::floor(sx);
uint xr = std::floor(sx + 1);
uint yt = std::floor(sy);
uint yb = std::floor(sy + 1);
for (uint d = 0; d < image->GetDepth(); ++d)
{
uchar tl = GetData(xl, yt, d);
uchar tr = GetData(xr, yt, d);
uchar bl = GetData(xl, yb, d);
uchar br = GetData(xr, yb, d);
double t = lerp(tl, tr, xl, xr, sx);
double b = lerp(bl, br, xl, xr, sx);
double m = lerp(t, b, yt, yb, sy);
uchar val = std::floor(m + 0.5);
image->SetData(x,y,d,val);
}
}
}
//Cleanup
mWidth = width; mHeight = height;
std::swap(image->mData, mData);
}
Input Image (4 pixels wide and high)
My Output
Expected Output (Photoshop's Bilinear Interpolation)
Photoshop's algorithm assumes that each source pixel's color is in the center of the pixel, while your algorithm assumes that the color is in its topleft. This causes your results to be shifted half a pixel up and left compared to Photoshop.
Another way to look at it is that your algorithm maps the x coordinate range (0, srcWidth) to (0, dstWidth), while Photoshop maps (-0.5, srcWidth-0.5) to (-0.5, dstWidth-0.5), and the same in y coordinate.
Instead of:
double sx = x * rx;
double sy = y * ry;
You can use:
double sx = (x + 0.5) * rx - 0.5;
double sy = (y + 0.5) * ry - 0.5;
to get similar results. Note that this can give you a negative value for sx and sy.

c++ Bresenham's line algorithm draw arc and rotate

I'm searching way to make arc with Bresenham's line algorithm. This algoritm draw perfect circle, but what if i need draw arc (from 0 to Pi) and rotate it for 30 degrees (for example)?
void DrawCircle(HDC hdc,int x0, int y0, int radius)
{
int x = 0;
int y = radius;
int delta = 2 - 2 * radius;
int error = 0;
while(y >= 0) {
//SetPixel(hdc,x0 + x, y0 + y,pencol);
SetPixel(hdc,x0 + x, y0 - y,pencol);
//SetPixel(hdc,x0 - x, y0 + y,pencol);
SetPixel(hdc,x0 - x, y0 - y,pencol);
error = 2 * (delta + y) - 1;
if(delta < 0 && error <= 0) {
++x;
delta += 2 * x + 1;
continue;
}
error = 2 * (delta - x) - 1;
if(delta > 0 && error > 0) {
--y;
delta += 1 - 2 * y;
continue;
}
++x;
delta += 2 * (x - y);
--y;
}
}
To get 1/2 a circle (to pi), only call one of your SetPixel routines. To have your arc rotated 30 degrees requires some trig. You could let the above loop run until your x/y ratio is equal to tan(30 degrees), then start actually drawing until your ratio hits the value at which you want to stop. Not the most efficient way, but it will work. To get it better, you'd need to pre-calculate your starting 4 var values. You could take the values from the above run and plug them in as starting values and that would be very efficient.
Did you get the above algorithm from Michael Abrash's Black Book stuff? If not, I'd google for that as a second point of reference on fast circle/arc drawing.
Well, alas, the ellipses that rip chapter wasn't included in there. Here's something I found on the web that claims to be from Abrash:
/* One of Abrash's ellipse algorithms */
void draw_ellipse(int x, int y, int a, int b, int color)
{
int wx, wy;
int thresh;
int asq = a * a;
int bsq = b * b;
int xa, ya;
draw_pixel(x, y+b, color);
draw_pixel(x, y-b, color);
wx = 0;
wy = b;
xa = 0;
ya = asq * 2 * b;
thresh = asq / 4 - asq * b;
for (;;) {
thresh += xa + bsq;
if (thresh >= 0) {
ya -= asq * 2;
thresh -= ya;
wy--;
}
xa += bsq * 2;
wx++;
if (xa >= ya)
break;
draw_pixel(x+wx, y-wy, color);
draw_pixel(x-wx, y-wy, color);
draw_pixel(x+wx, y+wy, color);
draw_pixel(x-wx, y+wy, color);
}
draw_pixel(x+a, y, color);
draw_pixel(x-a, y, color);
wx = a;
wy = 0;
xa = bsq * 2 * a;
ya = 0;
thresh = bsq / 4 - bsq * a;
for (;;) {
thresh += ya + asq;
if (thresh >= 0) {
xa -= bsq * 2;
thresh = thresh - xa;
wx--;
}
ya += asq * 2;
wy++;
if (ya > xa)
break;
draw_pixel(x+wx, y-wy, color);
draw_pixel(x-wx, y-wy, color);
draw_pixel(x+wx, y+wy, color);
draw_pixel(x-wx, y+wy, color);
}
}
The idea being you draw an 8th of the circle at a time x4 and then flip to get the other 8ths drawn. Still doesn't directly answer your question though. Working on that...
Again, your code above should work, you just need to control the starting and ending conditions carefully. The y >= 0 needs to become whatever the y would be upon finishing your 'arc' length and the starting values need to be calculated to be the start of your arc.
This will not be a straight forward task with things as they are. Might just be easier to use a floating point routine instead. The math is much more straight forward and processors tend to handle them better now than when these integer routines were crafted.
If you don't need for sure Bresenham, there is a fast step method introduced in this SO post, where you can set center point, starting point and arc angle. It doesn't need stopping criterion, because it is already included in algorithm (by arc angle). What makes it fast is the precalculation of tangential and radial movement factors and the actual loop has no trig function calls, only multiply, add and subtract.
AFAIK there is three types of methods:
A) Incremental like Bresenham
B) Subdivide method like this
C) Step (or segment) method
I'll take an slow example of step method (don't use this if speed is important):
// I know the question is tagged c++, but the idea comes clear in javascript
var start_angle = 0.5, end_angle = 1.1, r = 30;
for(var i = start_angle; i < end_angle; i = i + 0.05)
{
drawpixel(x: 50 + Math.cos(i) * r, y: 100 + Math.sin(i) * r); // center point is (x = 50, y = 100)
}
The slowness comes from cos and sin which are repeated (unnecessarily) in loop. This can be solved by precalculating cos and sin as described in the above mentioned SO post. This means huge speedup (average 12x in top5 javascript engines).
I made a non-full-comparable speedtest of various circle and arc drawing algorithms. The Bresenham is fast, but the starting and stopping criterion logic need to be added, which slows down the algo a little. If you really need Bresenham and arc, I have no ready solution for this and not found such yet. It surely is possible. By the way, the step method using precalculated trigs is not so bad in performance compared to Bresenham (in javascript at least). Please test in c++ and report.

Can normal maps be generated from a texture?

If I have a texture, is it then possible to generate a normal-map for this texture, so it can be used for bump-mapping?
Or how are normal maps usually made?
Yes. Well, sort of. Normal maps can be accurately made from height-maps. Generally, you can also put a regular texture through and get decent results as well. Keep in mind there are other methods of making a normal map, such as taking a high-resolution model, making it low resolution, then doing ray casting to see what the normal should be for the low-resolution model to simulate the higher one.
For height-map to normal-map, you can use the Sobel Operator. This operator can be run in the x-direction, telling you the x-component of the normal, and then the y-direction, telling you the y-component. You can calculate z with 1.0 / strength where strength is the emphasis or "deepness" of the normal map. Then, take that x, y, and z, throw them into a vector, normalize it, and you have your normal at that point. Encode it into the pixel and you're done.
Here's some older incomplete-code that demonstrates this:
// 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;
}
There's probably many ways to generate a Normal map, but like others said, you can do it from a Height Map, and 3d packages like XSI/3dsmax/Blender/any of them can output one for you as an image.
You can then output and RGB image with the Nvidia plugin for photoshop, an algorithm to convert it or you might be able to output it directly from those 3d packages with 3rd party plugins.
Be aware that in some case, you might need to invert channels (R, G or B) from the generated normal map.
Here's some resources link with examples and more complete explanation:
http://developer.nvidia.com/object/photoshop_dds_plugins.html
http://en.wikipedia.org/wiki/Normal_mapping
http://www.vrgeo.org/fileadmin/VRGeo/Bilder/VRGeo_Papers/jgt2002normalmaps.pdf
I don't think normal maps are generated from a texture. they are generated from a model.
just as texturing allows you to define complex colour detail with minimal polys (as opposed to just using millions of ploys and just vertex colours to define the colour on your mesh)
A normal map allows you to define complex normal detail with minimal polys.
I believe normal maps are usually generated from a higher res mesh, and then is used with a low res mesh.
I'm sure 3D tools, such as 3ds max or maya, as well as more specific tools will do this for you. unlike textures, I don't think they are usually done by hand.
but they are generated from the mesh, not the texture.
I suggest starting with OpenCV, due to its richness in algorithms. Here's one I wrote that iteratively blurs the normal map and weights those to the overall value, essentially creating more of a topological map.
#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;
}