Merge pull request #3889 from akohlmey/image-anti-alias

Implement anti-aliasing for dump image (and dump movie)
This commit is contained in:
Axel Kohlmeyer
2023-08-18 15:52:01 -04:00
committed by GitHub
9 changed files with 119 additions and 26 deletions

BIN
doc/src/JPG/image.both.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
doc/src/JPG/image.fsaa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
doc/src/JPG/image.ssao.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -24,7 +24,7 @@ Syntax
* color = atom attribute that determines color of each atom * color = atom attribute that determines color of each atom
* diameter = atom attribute that determines size of each atom * diameter = atom attribute that determines size of each atom
* zero or more keyword/value pairs may be appended * zero or more keyword/value pairs may be appended
* keyword = *atom* or *adiam* or *bond* or *grid* or *line* or *tri* or *body* or *fix* or *size* or *view* or *center* or *up* or *zoom* or *box* or *axes* or *subbox* or *shiny* or *ssao* * keyword = *atom* or *adiam* or *bond* or *grid* or *line* or *tri* or *body* or *fix* or *size* or *view* or *center* or *up* or *zoom* or *box* or *axes* or *subbox* or *shiny* or *fsaa* or *ssao*
.. parsed-literal:: .. parsed-literal::
@ -85,6 +85,8 @@ Syntax
diam = diameter of subdomain lines as fraction of shortest box length diam = diameter of subdomain lines as fraction of shortest box length
*shiny* value = sfactor = shinyness of spheres and cylinders *shiny* value = sfactor = shinyness of spheres and cylinders
sfactor = shinyness of spheres and cylinders from 0.0 to 1.0 sfactor = shinyness of spheres and cylinders from 0.0 to 1.0
*fsaa* arg = yes/no
yes/no = do or do not apply anti-aliasing
*ssao* value = shading seed dfactor = SSAO depth shading *ssao* value = shading seed dfactor = SSAO depth shading
shading = *yes* or *no* = turn depth shading on/off shading = *yes* or *no* = turn depth shading on/off
seed = random # seed (positive integer) seed = random # seed (positive integer)
@ -227,7 +229,7 @@ details have to be looked up in the `FFmpeg documentation
described below. described below.
To write out JPEG and PNG format files, you must build LAMMPS with To write out JPEG and PNG format files, you must build LAMMPS with
support for the corresponding JPEG or PNG library. To convert images support for the corresponding JPEG or PNG library. To convert images
into movies, LAMMPS has to be compiled with the -DLAMMPS_FFMPEG into movies, LAMMPS has to be compiled with the -DLAMMPS_FFMPEG
flag. See the :doc:`Build settings <Build_settings>` page for flag. See the :doc:`Build settings <Build_settings>` page for
details. details.
@ -597,13 +599,47 @@ image will appear. The *sfactor* value must be a value 0.0 <=
*sfactor* <= 1.0, where *sfactor* = 1 is a highly reflective surface *sfactor* <= 1.0, where *sfactor* = 1 is a highly reflective surface
and *sfactor* = 0 is a rough non-shiny surface. and *sfactor* = 0 is a rough non-shiny surface.
The *ssao* keyword turns on/off a screen space ambient occlusion .. versionadded:: TBD
(SSAO) model for depth shading. If *yes* is set, then atoms further
away from the viewer are darkened via a randomized process, which is The *fsaa* keyword can be used with the dump image command to improve
perceived as depth. The calculation of this effect can increase the the image quality by enabling full scene anti-aliasing. Internally the
cost of computing the image by roughly 2x. The strength of the effect image is rendered at twice the width and height and then scaled down by
can be scaled by the *dfactor* parameter. If *no* is set, no depth computing the average of each 2x2 block of pixels to produce a single
shading is performed. pixel in the final image at the original size. This produces images with
smoother, less ragged edges. The application of this algorithm can
increase the cost of computing the image by about 3x or more.
The *ssao* keyword turns on/off a screen space ambient occlusion (SSAO)
model for depth shading. If *yes* is set, then atoms further away from
the viewer are darkened via a randomized process, which is perceived as
depth. The strength of the effect can be scaled by the *dfactor*
parameter. If *no* is set, no depth shading is performed. The
calculation of this effect can increase the cost of computing the image
substantially by 5x or more, especially with larger images. When used
in combination with the *fsaa* keyword the computational cost of depth
shading is particularly large.
----------
Image Quality Settings
""""""""""""""""""""""
The two keywords *fsaa* and *ssao* can be used to improve the image
quality at the expense of additional computational cost to render the
images. The images below show from left to right the same render with
default settings, with *fsaa* added, with *ssao* added, and with both
keywords added.
.. |imagequality1| image:: JPG/image.default.png
:width: 24%
.. |imagequality2| image:: JPG/image.fsaa.png
:width: 24%
.. |imagequality3| image:: JPG/image.ssao.png
:width: 24%
.. |imagequality4| image:: JPG/image.both.png
:width: 24%
|imagequality1| |imagequality2| |imagequality3| |imagequality4|
---------- ----------
@ -1051,6 +1087,7 @@ The defaults for the dump_modify keywords specific to dump image and dump movie
* boxcolor = yellow * boxcolor = yellow
* color = 140 color names are pre-defined as listed below * color = 140 color names are pre-defined as listed below
* framerate = 24 * framerate = 24
* fsaa = no
* gmap = min max cf 0.0 2 min blue max red * gmap = min max cf 0.0 2 min blue max red
---------- ----------

View File

@ -1195,6 +1195,7 @@ Freitas
Frenkel Frenkel
Friedrichs Friedrichs
fs fs
fsaa
fsh fsh
fstyle fstyle
fsw fsw

View File

@ -248,10 +248,14 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) :
if (iarg+3 > narg) error->all(FLERR,"Illegal dump image command"); if (iarg+3 > narg) error->all(FLERR,"Illegal dump image command");
int width = utils::inumeric(FLERR,arg[iarg+1],false,lmp); int width = utils::inumeric(FLERR,arg[iarg+1],false,lmp);
int height = utils::inumeric(FLERR,arg[iarg+2],false,lmp); int height = utils::inumeric(FLERR,arg[iarg+2],false,lmp);
if (width <= 0 || height <= 0) if (width <= 0 || height <= 0) error->all(FLERR,"Illegal dump image command");
error->all(FLERR,"Illegal dump image command"); if (image->fsaa) {
image->width = width; image->width = width*2;
image->height = height; image->height = height*2;
} else {
image->width = width;
image->height = height;
}
iarg += 3; iarg += 3;
} else if (strcmp(arg[iarg],"view") == 0) { } else if (strcmp(arg[iarg],"view") == 0) {
@ -345,6 +349,23 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) :
image->shiny = shiny; image->shiny = shiny;
iarg += 2; iarg += 2;
} else if (strcmp(arg[iarg],"fsaa") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Illegal dump_modify command");
int aa = utils::logical(FLERR, arg[iarg+1], false, lmp);
if (aa) {
if (!image->fsaa) {
image->width = image->width*2;
image->height = image->height*2;
}
} else {
if (image->fsaa) {
image->width = image->width/2;
image->height = image->height/2;
}
}
image->fsaa = aa;
iarg += 2;
} else if (strcmp(arg[iarg],"ssao") == 0) { } else if (strcmp(arg[iarg],"ssao") == 0) {
if (iarg+4 > narg) error->all(FLERR,"Illegal dump image command"); if (iarg+4 > narg) error->all(FLERR,"Illegal dump image command");
image->ssao = utils::logical(FLERR,arg[iarg+1],false,lmp); image->ssao = utils::logical(FLERR,arg[iarg+1],false,lmp);

View File

@ -55,7 +55,9 @@ enum{NO,YES};
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
Image::Image(LAMMPS *lmp, int nmap_caller) : Pointers(lmp) Image::Image(LAMMPS *lmp, int nmap_caller) :
Pointers(lmp), depthBuffer(nullptr), surfaceBuffer(nullptr), depthcopy(nullptr),
surfacecopy(nullptr), imageBuffer(nullptr), rgbcopy(nullptr), writeBuffer(nullptr)
{ {
MPI_Comm_rank(world,&me); MPI_Comm_rank(world,&me);
MPI_Comm_size(world,&nprocs); MPI_Comm_size(world,&nprocs);
@ -69,6 +71,7 @@ Image::Image(LAMMPS *lmp, int nmap_caller) : Pointers(lmp)
persp = 0.0; persp = 0.0;
shiny = 1.0; shiny = 1.0;
ssao = NO; ssao = NO;
fsaa = NO;
up[0] = 0.0; up[0] = 0.0;
up[1] = 0.0; up[1] = 0.0;
@ -154,6 +157,13 @@ Image::~Image()
void Image::buffers() void Image::buffers()
{ {
memory->destroy(depthBuffer);
memory->destroy(surfaceBuffer);
memory->destroy(imageBuffer);
memory->destroy(depthcopy);
memory->destroy(surfacecopy);
memory->destroy(rgbcopy);
npixels = width * height; npixels = width * height;
memory->create(depthBuffer,npixels,"image:depthBuffer"); memory->create(depthBuffer,npixels,"image:depthBuffer");
memory->create(surfaceBuffer,2*npixels,"image:surfaceBuffer"); memory->create(surfaceBuffer,2*npixels,"image:surfaceBuffer");
@ -380,6 +390,26 @@ void Image::merge()
} else { } else {
writeBuffer = imageBuffer; writeBuffer = imageBuffer;
} }
// scale down image for antialiasing. can be done in place with simple averaging
if (fsaa) {
for (int h=0; h < height; h += 2) {
for (int w=0; w < width; w +=2) {
int idx1 = 3*height*h + 3*w;
int idx2 = 3*height*h + 3*(w+1);
int idx3 = 3*height*(h+1) + 3*w;
int idx4 = 3*height*(h+1) + 3*(w+1);
int out = 3*(height/2)*(h/2) + 3*(w/2);
for (int i=0; i < 3; ++i) {
writeBuffer[out+i] = (unsigned char) (0.25*((int)writeBuffer[idx1+i]
+(int)writeBuffer[idx2+i]
+(int)writeBuffer[idx3+i]
+(int)writeBuffer[idx4+i]));
}
}
}
}
} }
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
@ -1037,6 +1067,7 @@ void Image::compute_SSAO()
void Image::write_JPG(FILE *fp) void Image::write_JPG(FILE *fp)
{ {
#ifdef LAMMPS_JPEG #ifdef LAMMPS_JPEG
int aafactor = fsaa ? 2 : 1;
struct jpeg_compress_struct cinfo; struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr; struct jpeg_error_mgr jerr;
JSAMPROW row_pointer; JSAMPROW row_pointer;
@ -1044,8 +1075,8 @@ void Image::write_JPG(FILE *fp)
cinfo.err = jpeg_std_error(&jerr); cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo); jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo,fp); jpeg_stdio_dest(&cinfo,fp);
cinfo.image_width = width; cinfo.image_width = width/aafactor;
cinfo.image_height = height; cinfo.image_height = height/aafactor;
cinfo.input_components = 3; cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB; cinfo.in_color_space = JCS_RGB;
@ -1055,7 +1086,7 @@ void Image::write_JPG(FILE *fp)
while (cinfo.next_scanline < cinfo.image_height) { while (cinfo.next_scanline < cinfo.image_height) {
row_pointer = (JSAMPROW) row_pointer = (JSAMPROW)
&writeBuffer[(cinfo.image_height - 1 - cinfo.next_scanline) * 3 * width]; &writeBuffer[(cinfo.image_height - 1 - cinfo.next_scanline) * 3 * (width/aafactor)];
jpeg_write_scanlines(&cinfo,&row_pointer,1); jpeg_write_scanlines(&cinfo,&row_pointer,1);
} }
@ -1071,6 +1102,7 @@ void Image::write_JPG(FILE *fp)
void Image::write_PNG(FILE *fp) void Image::write_PNG(FILE *fp)
{ {
#ifdef LAMMPS_PNG #ifdef LAMMPS_PNG
int aafactor = fsaa ? 2 : 1;
png_structp png_ptr; png_structp png_ptr;
png_infop info_ptr; png_infop info_ptr;
@ -1090,8 +1122,8 @@ void Image::write_PNG(FILE *fp)
} }
png_init_io(png_ptr, fp); png_init_io(png_ptr, fp);
png_set_compression_level(png_ptr,Z_BEST_COMPRESSION); png_set_compression_level(png_ptr,Z_BEST_SPEED);
png_set_IHDR(png_ptr,info_ptr,width,height,8,PNG_COLOR_TYPE_RGB, png_set_IHDR(png_ptr,info_ptr,width/aafactor,height/aafactor,8,PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
png_text text_ptr[2]; png_text text_ptr[2];
@ -1111,9 +1143,9 @@ void Image::write_PNG(FILE *fp)
png_set_text(png_ptr,info_ptr,text_ptr,1); png_set_text(png_ptr,info_ptr,text_ptr,1);
png_write_info(png_ptr,info_ptr); png_write_info(png_ptr,info_ptr);
auto row_pointers = new png_bytep[height]; auto row_pointers = new png_bytep[height/aafactor];
for (int i=0; i < height; ++i) for (int i=0; i < height/aafactor; ++i)
row_pointers[i] = (png_bytep) &writeBuffer[(height-i-1)*3*width]; row_pointers[i] = (png_bytep) &writeBuffer[((height/aafactor)-i-1)*3*(width/aafactor)];
png_write_image(png_ptr, row_pointers); png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr); png_write_end(png_ptr, info_ptr);
@ -1129,11 +1161,12 @@ void Image::write_PNG(FILE *fp)
void Image::write_PPM(FILE *fp) void Image::write_PPM(FILE *fp)
{ {
fprintf(fp,"P6\n%d %d\n255\n",width,height); int aafactor = fsaa ? 2 : 1;
fprintf(fp,"P6\n%d %d\n255\n",width/aafactor,height/aafactor);
int y; int y;
for (y = height-1; y >= 0; y--) for (y = (height/aafactor)-1; y >= 0; y--)
fwrite(&writeBuffer[y*width*3],3,width,fp); fwrite(&writeBuffer[y*(width/aafactor)*3],3,width/aafactor,fp);
} }
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------

View File

@ -28,13 +28,14 @@ class Image : protected Pointers {
double zoom; // zoom factor double zoom; // zoom factor
double persp; // perspective factor double persp; // perspective factor
double shiny; // shininess of objects double shiny; // shininess of objects
int fsaa; // antialiasing on or off
int ssao; // SSAO on or off int ssao; // SSAO on or off
int seed; // RN seed for SSAO int seed; // RN seed for SSAO
double ssaoint; // strength of shading from 0 to 1 double ssaoint; // strength of shading from 0 to 1
double *boxcolor; // color to draw box outline with double *boxcolor; // color to draw box outline with
int background[3]; // RGB values of background int background[3]; // RGB values of background
double ambientColor[3]; // light color settings (adjustable by caller) double ambientColor[3]; // light color settings (adjustable by caller)
double keyLightColor[3]; double keyLightColor[3];
double fillLightColor[3]; double fillLightColor[3];
double backLightColor[3]; double backLightColor[3];