Pixel previewing is a way of rendering a frame several times in sequence, with only shader parameters changing. It is intended for graphical front-ends that allow the user to tune shader parameters such as object colors with maximum turnaround.
For this purpose, raylib supports a preview mode that retains the frame buffers between successive frames, and collects information about which shaders have contributed to which pixels. When the next frame is rendered, a shader bitmap can be passed that tells raylib which shaders have changed parameters. It then re-renders only those pixels that are affected, and leaves all other pixels unchanged. If a screen sub-rectangle contains no changed pixels, the entire rectangle is omitted.
This works for all types of shader changes except those affecting geometry (geometry and displacement shaders), and except output shaders. (Tuning output shaders requires no re-rendering at all; this case is handled with mi_rc_run's miRENDER_FB_* modes.) Pixel previewing does, however, work with full ray tracing (transparency, reflectivity, volumes, etc). In principle, it also works with global illumination and lens shaders, but there is usually little benefit because these shaders tend to touch all or most pixels.
The standard sequence of steps for pixel previewing is
Shader labels are assigned by raylib when shaders are created. Step 3 is expected to create the changed-shader bitmap by reading the miFunction's label field. If the label is n, bit (n % 31) of the bitmap should be set. Since the bitmap has only 32 bits it is possible that too many pixels are rerendered, but never too few. To avoid accidentally hitting the bit of the lens shader, it may be a good idea to disable lens and atmosphere shaders in the options block.
The following example uses pixel previewing to render a scene with two cubes, a large cyan one and a small magenta one in front, then changes the magenta color to yellow, and rerenders. Only magenta pixels will be rerendered. The initialization code up to and including the call to mi_mi_parse_rayrc is identical to the previous examples.
{ ... mi_mi_parse_rayrc(0, miFALSE); if (!mi_mi_parse(argv[1], miFALSE, 0, 0, 0, getc, miFALSE, 0)) mi_fatal("parse error"); mi_api_render_params(&root, &caminst, &cam, &opt, &inh); /* * turn pixel previewing on */ ((miOptions *)mi_scene_edit(opt))->pixel_preview = miTRUE; mi_scene_edit_end(opt); /* * render first frame, standard magenta cube (mtl2) */ if (!mi_rc_run(miRENDER_PREPROC | miRENDER_OBJ_DELETE | miRENDER_SHADOWMAP | miRENDER_DISPLAY | miRENDER_RENDER, 0, 0, root, caminst, cam, opt, inh)) mi_fatal("render error"); /* * incremental change: change magenta to yellow */ mtl_tag = mi_api_name_lookup(mi_mem_strdup("mtl2")); mtl = mi_db_access(mtl_tag); func = mi_scene_edit(mtl->shader); if (func) { miParam_type type; int offs; if (mi_api_parameter_lookup(&type, 0, &offs, func->funtion_decl, miFALSE /* input */, mi_mem_strdup("diffuse")) && type == miTYPE_COLOR) { /* assuming there is no parameter connection */ miColor* diff = (miColor*)(func->parameters + offs); diff->r = 0.8; diff->g = 0.8; diff->b = 0.0; } label = func->label; } mi_scene_edit_end(mtl->shader); mi_db_unpin(mtl_tag); /* * render again (only changed pixels will be rerendered) */ if (!mi_rc_run(miRENDER_RENDER, 0, 1 << (label & 31), root, caminst, cam, opt, inh)) mi_fatal("render error"); /* * turn pixel previewing off and postprocess (to delete frame bufs) */ ((miOptions *)mi_scene_edit(opt))->pixel_preview = miFALSE; mi_scene_edit_end(opt); if (!mi_rc_run(miRENDER_POSTPROC, 0, 0, root, caminst, cam, opt, inh)) mi_fatal("postprocess error"); mi_api_render_release(); mi_raylib_exit(); mi_raylib_detach_process(); return(0); }
The postprocessing call to mi_rc_run at the end of the pixel preview render loop is important because it releases the frame buffers and certain bitmap support buffers maintained by raylib for previewing. Failure to postprocess introduces a large memory leak. It is important to clear the pixel_preview flag in the options block before the postprocessing call.
To keep the example short, this code reads a scene file whose name must be passed as the first command-line argument (argv[1]). This file defines the two instances of a cube, with the front cube inheriting the material mtl2 that is incrementally changed. Here is the scene file:
$include <base.mi> options "opt" samples 0 0 object space end options camera "cam" framebuffer "main" filetype "rgb" filename "out.rgb" focal 50 aperture 44 end camera instance "cam_inst" "cam" transform 0.7719 0.3042 -0.5582 0.0 0.0000 0.8781 0.4785 0.0 0.6357 -0.3693 0.6778 0.0 0.0000 0.0000 -2.5000 1.0 end instance light "light1" "mib_light_point" ("color" 1 1 1) origin 0 0 0 end light instance "light1_inst" "light1" transform 1 0 0 0 0 1 0 0 0 0 1 0 -2 -3 -2 1 end instance material "mtl1" "mib_illum_phong" ( "ambient" 0.0 0.5 0.5, "diffuse" 0.0 0.7 0.7, "ambience" 0.3 0.3 0.3, "mode" 4 end material material "mtl2" "mib_illum_phong" ( "ambient" 0.5 0.0 0.5, "diffuse" 0.7 0.0 0.7, "ambience" 0.3 0.3 0.3, "mode" 4 end material object "cube1" visible on shadow 3 reflection 3 refraction 3 group -.5 -.5 -.5 -.5 -.5 .5 -.5 .5 -.5 -.5 .5 .5 .5 -.5 -.5 .5 -.5 .5 .5 .5 -.5 .5 .5 .5 v 0 v 1 v 2 v 3 v 4 v 5 v 6 v 7 p 0 1 3 2 p 1 5 7 3 p 5 4 6 7 p 4 0 2 6 p 4 5 1 0 p 2 3 7 6 end group end object instance "cube1_inst" "cube1" material "mtl1" light ["light1_inst"] transform 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 end instance instance "cube2_inst" "cube1" material "mtl2" light ["light1_inst"] transform 3 0 0 0 0 6 0 0 0 0 3 0 2 -2 -2 1 end instance instgroup "rootgrp" "cam_inst" "light1_inst" "cube1_inst" "cube2_inst" end instgroup render "rootgrp" "cam_inst" "opt"
When running this example with the imf_disp utility attached to the output image file out.rgb, only the screen sub-rectangles containing magenta pixels are redisplayed. Cyan pixels are also not recomputed but redisplayed because imf_disp always deals with complete subrectangles.
Copyright © 1986, 2015 NVIDIA ARC GmbH. All rights reserved.