\* This algorithm assumes that the surface to be rendered is marked by 1's in bit0 of subcell 0. A rendered image will be constructed and displayed, and when done, all data in CAM will be restored to what it was before the rendering. During rendering, we accumulate attenuation counts along two optical paths -- the inward path and the return or outward path. We increment these counts whenever we find matter as we traverse slices of z=constant for increasing z. Whenever we find matter, we also use these two counts in order to determine a value to add to a visibility count: a spot of matter contributes visibility if neither the inward nor outward attenuation is saturated. The amount of contribution depends on both of these counts. When we reach the last z-slice, we determine at which spots the background color is visible. We do the rendering in one pass. We begin by saving the contents of the z=0 slice, and initilizing a replacement slice that will be scanned accross the space. After rendering, we display this slice and then restore it to its original value. Note that these routines assume that modules are not connected in the z-direction; i.e., they assume that the z-direction is an internal dimension. *\ 0 0 == matter 0 0 == background 1 7 == visibility 8 10 == attenuation-in 11 13 == attenuation-out 14 14 == rand-bit 7 constant attenuation-max 127 constant visibility-max 0 constant render-using-depth : rend-rule \ Attenuate and compute visibility wherever there is matter. \ All other places don't change these values. If any of these \ values get too big, they saturate at their max values. matter if \ attenuation-in attenuation-max = \ attenuation-out 0<> and \ if \ 1 1 \ else 1 attenuation-in \ then attenuation-out + << visibility-max 56 * 100 / swap / visibility + visibility-max min -> visibility attenuation-in 1+ attenuation-max min -> attenuation-in attenuation-out 1+ attenuation-max min -> attenuation-out then \ The site source is normally set so that "matter" is not \ changed. To compute the background bit, we run the final \ scan with the new source for "matter" set to be the lut. update attenuation-out 0= -> background \ same as "matter" ; create-lut rend-table ?rule>table rend-rule rend-table 6 create-buffer display-save-lut-src 6 create-buffer display-save-site-src 24 create-buffer display-save-offset \* Before rendering, we save state, including the currently inactive lookup table (since we're going to use our own table) and the current offset (since restoring the data involves also restoring the offsets caused by our kicks, so that all data is back where it started). For the first scan, we fix all of our rendering variables at 0: in subsequent scans we will accumulate data in these variables. We also setup to scan a 2D slice at a time. *\ define-step init-rend-step select read *select-buf select 0 module lut-data read display-save-table lut-src read display-save-lut-src site-src read display-save-site-src offset read display-save-offset select *select-buf lut-data rend-table switch-luts site-src lut matter field site lut-src site attenuation-in field 0 fix attenuation-out field 0 fix visibility field 0 fix U by V by..1 subsector kick end-step \* We kick the data in the z-direction starting with the second scan of the space, so that our accumulator is the sheet of data that started at z=0. We scan the remainder of the space looking at and modifying this accumulator sheet. Note that we allocate and kick around a random-bit field, but this random field is at present neither initialized nor used by the rendering rule. It is included only to illustrate how such a random field would be implemented. *\ 1 constant amp amp negate constant -amp define-step rendu-step \ visibility comes from upper eye run kick attenuation-in field 1 x 1 y 1 z attenuation-out field amp y 1 z visibility field amp y 1 z rand-bit field 13 x 29 y 1 z lut-src site end-step define-step rendl-step \ visibility comes from lower eye run kick attenuation-in field 1 x 1 y 1 z attenuation-out field -amp y 1 z visibility field -amp y 1 z rand-bit field 13 x 29 y 1 z lut-src site end-step define-step rend0-step \ visibility comes straight in run kick attenuation-in field 1 x 1 y 1 z attenuation-out field 0 y 1 z visibility field 0 y 1 z rand-bit field 13 x 29 y 1 z lut-src site end-step \* After running scans for each value of z, we have our accumulated results in the last z-slice of the space. We do one more scan to shift our data into the position z=0, and during this step we compute the "background" bit. Then we restore the saved table and parameters. *\ define-step finish-rend-step run site-src site background field lut kick attenuation-in field 1 z attenuation-out field 1 z visibility field 1 z rand-bit field 1 z run switch-luts lut-data display-save-table lut-src display-save-lut-src site-src display-save-site-src display site full-space end-step \* If no display is selected, we skip the rendering step. If the z-dimension is not at least 2 deep, we just display a 2d slice instead of rendering. These rountines assume that the z-dimension is an internal dimension; if the z-dimension is split between several modules, we just give an error message. If its okay to render, we do any special "before-display" actions, then we save the z=0 slice, render, show the rendered image, restore CAM parameters and tables, restore the z=0 slice, and finally perform any "after-display" actions. *\ 0 constant rend0 \ number of rend0 steps per cycle 1 constant rendu \ number of rendu steps per cycle (from below) 0 constant rendl \ number of rendl steps per cycle (from above) : rend-cyc (s -- n ) rend0 rendu rendl + + ; : render display? not if exit then Z 2 < if (show) exit then W Z <> abort" Incompatible module interconnect topology!" before-display X Y * ['] pattern change-reglen step pattern 0 2d-slice>pattern init-rend-step W 1- 0 ?do i rend-cyc mod dup rend0 < if rend0-step else dup rend0 rendu + < if rendu-step else dup rend-cyc < if rendl-step then then then drop loop finish-rend-step ?regenerate-display display-step ?image>xmon offset display-save-offset step 0 pattern>2d-slice after-display ; centering-hd off full-size \ Center for slices, but not when rendering \* A good color map for rendering. It shows matter in gold, shadows in black, and the background in blue. *\ true constant show-background 0 constant lolite : rend-map background 0= if visibility lolite max 100 * 50 / >red visibility lolite max 100 * 70 / >green visibility lolite max 100 * 180 / >blue else show-background 0<> bright and 3 * 4 / >blue then ; DISPLAY-KEYS key-bindings : Toggle.background show-background not dup is show-background .on/off colormap rend-map ; press _ "Toggle background." palette-length create-buffer save-for-rendering : Toggle.rendering render-using-depth not dup is render-using-depth dup .on/off if ['] render is show ['] palette ['] save-for-rendering copy-buffer colormap rend-map else ['] (show) is show ['] save-for-rendering ['] palette copy-buffer update-cmap then show ; press * "Render 3D image with optical depth." : Jiggle.image begin 1 is rend0 0 is rendl 0 is rendu show 8 is rend0 0 is rendl 1 is rendu show 4 is rend0 0 is rendl 1 is rendu show 4 is rend0 0 is rendl 1 is rendu show 8 is rend0 0 is rendl 1 is rendu show 1 is rend0 0 is rendl 0 is rendu show 8 is rend0 1 is rendl 0 is rendu show 4 is rend0 1 is rendl 0 is rendu show 4 is rend0 1 is rendl 0 is rendu show 8 is rend0 1 is rendl 0 is rendu show key? until ; press j "Jiggle image up and down." 0 constant which-rend : increase-an-eye arg? if arg is which-rend then which-rend 0= if rend0 1+ is rend0 rend0 . then which-rend 1 = if rendu 1+ is rendu rendu . then which-rend 2 = if rendl 1+ is rendl rendl . then show ; press ) "Increase one of the rendering values." : decrease-an-eye arg? if arg is which-rend then which-rend 0= if rend0 1- is rend0 rend0 . then which-rend 1 = if rendu 1- is rendu rendu . then which-rend 2 = if rendl 1- is rendl rendl . then show ; press ( "Decrease one of the rendering values." EXPERIMENT-KEYS key-bindings 1 is rend0 1 is rendu 0 is rendl \ A nice viewpoint, especially if one is just sitting back and \ watching the plaque grow