Josh Posted April 24, 2023 Share Posted April 24, 2023 I have always loved the normal maps ATItoDOT3 produces. Here is an implementation using their algorithm grabbed from here: https://github.com/mojocorp/ATINormalMapper/blob/master/TGAtoDOT3/TGAtoDOT3.cpp #include "UltraEngine.h" using namespace UltraEngine; typedef struct _pixel { uint8 red; uint8 blue; uint8 green; uint8 alpha; } pixel; inline void TGAReadPixel(uint8* image, int width, int off, pixel* pix, int x, int y) { #ifdef _DEBUG if ((image == NULL) || (pix == NULL)) { //NmPrint("ERROR: NULL pointer passed to Readpixel!\n"); exit(-1); } #endif int idx = y * width * off + x * off; if (off > 0) { pix->red = image[idx]; } if (off > 1) { pix->blue = image[idx + 1]; } if (off > 2) { pix->green = image[idx + 2]; } } int gWidth, gHeight; void WritePixel(uint8* image, int bpp, const pixel* pix, int x, int y) { const int idx = (x + y * gWidth) * (bpp / 8); if (bpp >= 8) { image[idx + 0] = pix->blue; } if (bpp >= 16) { image[idx + 1] = pix->green; } if (bpp >= 24) { image[idx + 2] = pix->red; } if (bpp >= 32) { image[idx + 3] = pix->alpha; } } uint8 PackFloatInByte(float in) { return (uint8)((in + 1.0f) / 2.0f * 255.0f); } void SobelFilter(uint8* dstImage, uint8* srcImage, int width, int height, int bpp) { gWidth = width; gHeight = height; pixel pix; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // Do Y Sobel filter TGAReadPixel(srcImage, width, bpp / 8, &pix, (x - 1 + width) % width, (y + 1) % height); float dY = ((float)pix.red) / 255.0f * -1.0f; TGAReadPixel(srcImage, width, bpp / 8, &pix, x % width, (y + 1) % height); dY += ((float)pix.red) / 255.0f * -2.0f; TGAReadPixel(srcImage, width, bpp / 8, &pix, (x + 1) % width, (y + 1) % height); dY += ((float)pix.red) / 255.0f * -1.0f; TGAReadPixel( srcImage, width, bpp / 8, &pix, (x - 1 + width) % width, (y - 1 + height) % height); dY += ((float)pix.red) / 255.0f * 1.0f; TGAReadPixel(srcImage, width, bpp / 8, &pix, x % width, (y - 1 + height) % height); dY += ((float)pix.red) / 255.0f * 2.0f; TGAReadPixel( srcImage, width, bpp / 8, &pix, (x + 1) % width, (y - 1 + height) % height); dY += ((float)pix.red) / 255.0f * 1.0f; // Do X Sobel filter TGAReadPixel( srcImage, width, bpp / 8, &pix, (x - 1 + width) % width, (y - 1 + height) % height); float dX = ((float)pix.red) / 255.0f * -1.0f; TGAReadPixel(srcImage, width, bpp / 8, &pix, (x - 1 + width) % width, y % height); dX += ((float)pix.red) / 255.0f * -2.0f; TGAReadPixel(srcImage, width, bpp / 8, &pix, (x - 1 + width) % width, (y + 1) % height); dX += ((float)pix.red) / 255.0f * -1.0f; TGAReadPixel( srcImage, width, bpp / 8, &pix, (x + 1) % width, (y - 1 + height) % height); dX += ((float)pix.red) / 255.0f * 1.0f; TGAReadPixel(srcImage, width, bpp / 8, &pix, (x + 1) % width, y % height); dX += ((float)pix.red) / 255.0f * 2.0f; TGAReadPixel(srcImage, width, bpp / 8, &pix, (x + 1) % width, (y + 1) % height); dX += ((float)pix.red) / 255.0f * 1.0f; // Cross Product of components of gradient reduces to float nX = -dX; float nY = -dY; float nZ = 1; // Normalize float oolen = 1.0f / ((float)sqrt(nX * nX + nY * nY + nZ * nZ)); nX *= oolen; nY *= oolen; nZ *= oolen; pix.red = (uint8)PackFloatInByte(nX); pix.green = (uint8)PackFloatInByte(nY); pix.blue = (uint8)PackFloatInByte(nZ); pix.alpha = 255; pix.red = 128; WritePixel(dstImage, bpp, &pix, x, y); } } } int main(int argc, const char* argv[]) { auto src = LoadPixmap("https://github.com/UltraEngine/Documentation/raw/master/Assets/Materials/Brick/brickwall01.dds"); if (src->format != TEXTURE_BGRA) src = src->Convert(TEXTURE_BGRA); auto dest = CreatePixmap(src->size.x, src->size.y, src->format); SobelFilter((uint8*)dest->pixels->Data(), (uint8*)src->pixels->Data(), src->size.x, src->size.y, 32); src->Save(GetPath(PATH_DESKTOP) + "/test.dds"); dest->Save(GetPath(PATH_DESKTOP) + "/testDOT3.dds"); return 0; } Input Output 2 Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.