diff --git a/doc/src/JPG/image.both.png b/doc/src/JPG/image.both.png new file mode 100644 index 0000000000..b74b0d773d Binary files /dev/null and b/doc/src/JPG/image.both.png differ diff --git a/doc/src/JPG/image.default.png b/doc/src/JPG/image.default.png new file mode 100644 index 0000000000..7acf837850 Binary files /dev/null and b/doc/src/JPG/image.default.png differ diff --git a/doc/src/JPG/image.fsaa.png b/doc/src/JPG/image.fsaa.png new file mode 100644 index 0000000000..9293b8c686 Binary files /dev/null and b/doc/src/JPG/image.fsaa.png differ diff --git a/doc/src/JPG/image.ssao.png b/doc/src/JPG/image.ssao.png new file mode 100644 index 0000000000..8270268dd0 Binary files /dev/null and b/doc/src/JPG/image.ssao.png differ diff --git a/doc/src/dump_image.rst b/doc/src/dump_image.rst index 25d1efff7d..4e227d2f72 100644 --- a/doc/src/dump_image.rst +++ b/doc/src/dump_image.rst @@ -24,7 +24,7 @@ Syntax * color = atom attribute that determines color of each atom * diameter = atom attribute that determines size of each atom * 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:: @@ -85,6 +85,8 @@ Syntax diam = diameter of subdomain lines as fraction of shortest box length *shiny* value = sfactor = shinyness of spheres and cylinders 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 shading = *yes* or *no* = turn depth shading on/off seed = random # seed (positive integer) @@ -227,7 +229,7 @@ details have to be looked up in the `FFmpeg documentation described below. 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 flag. See the :doc:`Build settings ` page for 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 and *sfactor* = 0 is a rough non-shiny surface. -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 calculation of this effect can increase the -cost of computing the image by roughly 2x. The strength of the effect -can be scaled by the *dfactor* parameter. If *no* is set, no depth -shading is performed. +.. versionadded:: TBD + +The *fsaa* keyword can be used with the dump image command to improve +the image quality by enabling full scene anti-aliasing. Internally the +image is rendered at twice the width and height and then scaled down by +computing the average of each 2x2 block of pixels to produce a single +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 * color = 140 color names are pre-defined as listed below * framerate = 24 +* fsaa = no * gmap = min max cf 0.0 2 min blue max red ---------- diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 3ed6fb8e42..bcf1e741c4 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -1195,6 +1195,7 @@ Freitas Frenkel Friedrichs fs +fsaa fsh fstyle fsw diff --git a/src/dump_image.cpp b/src/dump_image.cpp index 1ba433f93f..b1b91c6543 100644 --- a/src/dump_image.cpp +++ b/src/dump_image.cpp @@ -248,10 +248,14 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : if (iarg+3 > narg) error->all(FLERR,"Illegal dump image command"); int width = utils::inumeric(FLERR,arg[iarg+1],false,lmp); int height = utils::inumeric(FLERR,arg[iarg+2],false,lmp); - if (width <= 0 || height <= 0) - error->all(FLERR,"Illegal dump image command"); - image->width = width; - image->height = height; + if (width <= 0 || height <= 0) error->all(FLERR,"Illegal dump image command"); + if (image->fsaa) { + image->width = width*2; + image->height = height*2; + } else { + image->width = width; + image->height = height; + } iarg += 3; } else if (strcmp(arg[iarg],"view") == 0) { @@ -345,6 +349,23 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : image->shiny = shiny; 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) { if (iarg+4 > narg) error->all(FLERR,"Illegal dump image command"); image->ssao = utils::logical(FLERR,arg[iarg+1],false,lmp); diff --git a/src/image.cpp b/src/image.cpp index 0f31ae9927..c9e9960a3c 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -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_size(world,&nprocs); @@ -69,6 +71,7 @@ Image::Image(LAMMPS *lmp, int nmap_caller) : Pointers(lmp) persp = 0.0; shiny = 1.0; ssao = NO; + fsaa = NO; up[0] = 0.0; up[1] = 0.0; @@ -154,6 +157,13 @@ Image::~Image() void Image::buffers() { + memory->destroy(depthBuffer); + memory->destroy(surfaceBuffer); + memory->destroy(imageBuffer); + memory->destroy(depthcopy); + memory->destroy(surfacecopy); + memory->destroy(rgbcopy); + npixels = width * height; memory->create(depthBuffer,npixels,"image:depthBuffer"); memory->create(surfaceBuffer,2*npixels,"image:surfaceBuffer"); @@ -380,6 +390,26 @@ void Image::merge() } else { 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) { #ifdef LAMMPS_JPEG + int aafactor = fsaa ? 2 : 1; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; JSAMPROW row_pointer; @@ -1044,8 +1075,8 @@ void Image::write_JPG(FILE *fp) cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo,fp); - cinfo.image_width = width; - cinfo.image_height = height; + cinfo.image_width = width/aafactor; + cinfo.image_height = height/aafactor; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; @@ -1055,7 +1086,7 @@ void Image::write_JPG(FILE *fp) while (cinfo.next_scanline < cinfo.image_height) { 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); } @@ -1071,6 +1102,7 @@ void Image::write_JPG(FILE *fp) void Image::write_PNG(FILE *fp) { #ifdef LAMMPS_PNG + int aafactor = fsaa ? 2 : 1; png_structp png_ptr; png_infop info_ptr; @@ -1090,8 +1122,8 @@ void Image::write_PNG(FILE *fp) } png_init_io(png_ptr, fp); - png_set_compression_level(png_ptr,Z_BEST_COMPRESSION); - png_set_IHDR(png_ptr,info_ptr,width,height,8,PNG_COLOR_TYPE_RGB, + png_set_compression_level(png_ptr,Z_BEST_SPEED); + 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_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_write_info(png_ptr,info_ptr); - auto row_pointers = new png_bytep[height]; - for (int i=0; i < height; ++i) - row_pointers[i] = (png_bytep) &writeBuffer[(height-i-1)*3*width]; + auto row_pointers = new png_bytep[height/aafactor]; + for (int i=0; i < height/aafactor; ++i) + row_pointers[i] = (png_bytep) &writeBuffer[((height/aafactor)-i-1)*3*(width/aafactor)]; png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); @@ -1129,11 +1161,12 @@ void Image::write_PNG(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; - for (y = height-1; y >= 0; y--) - fwrite(&writeBuffer[y*width*3],3,width,fp); + for (y = (height/aafactor)-1; y >= 0; y--) + fwrite(&writeBuffer[y*(width/aafactor)*3],3,width/aafactor,fp); } /* ---------------------------------------------------------------------- diff --git a/src/image.h b/src/image.h index 51a0acc61c..4f87e92b67 100644 --- a/src/image.h +++ b/src/image.h @@ -28,13 +28,14 @@ class Image : protected Pointers { double zoom; // zoom factor double persp; // perspective factor double shiny; // shininess of objects + int fsaa; // antialiasing on or off int ssao; // SSAO on or off int seed; // RN seed for SSAO double ssaoint; // strength of shading from 0 to 1 double *boxcolor; // color to draw box outline with 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 fillLightColor[3]; double backLightColor[3];