Emacs: Random Bits 1

I have played more with elisp just a little bit and found it quite a simple language to work with.

What to look for

GNU Elisp documentation is simply awesome. It contains a lot of examples and information.

IRC #emacs channel on freenode is another source of help. People there are quick to respond and often answer within minutes.

Also paredit mode helps with parentheses as if it automatically writes matching ) when you write ( like I write (defun and it immediately adds ) at the end. Also it doesnt let you automatically remove either of parenthesis if there’s some content in between. In that case it moves cursor to the beginning of a word for you to delete first and then with one remove key deletes both parens.

Variable logger

When I use Lua I often call my middleware logging library to log out variables. I have made a similar thing but in the language itself in Javascript but it has limitations and is only for debugging purposes so making an editor spew out the needed log call is the way here.

The function is one of my first more real attempts to make somthing with elisp. I don’t care about optimizing it or so as I will call it once in a while only.

1
2
3
4
5
6
7
8
9
10
11
12
13
(defun lua-log(p1 p2)
  (interactive "r")
    (let (variables result)
      (setq variables (mapcar (lambda (var) (concat "'" var " = ', " var ", " )) (split-string (buffer-substring-no-properties p1 p2))) )
      (setq result (mapconcat 'identity (reverse(cdr (reverse variables))) ""))
      (setq result
        (concat
         result
         (substring (car (last variables)) 0 -2)))
      (delete-region p1 p2)
      (insert (format "log:warn(%s)" result))
   )
)

My function for logging is log:warn(x, y, z, ...) and I wanted to write x y z mark it and run command to generate the needed call.

So it begs for an explanation as in the other case there’s no point of writing about it:

  • defun name args defines a function with a name and list of arguments
  • interactive "r" means our function can be found with M-x and argument r means that it receives two
    parameters - start and end points of our selected region
  • let, setq, buffer-substring-no-properties, message were explained in previous post
  • mapcar applies a function to each element of a list
  • reverse returns reversed list
  • split-string splits passed string by passed separators. As we have not passed any it simply splits by whitespace
  • mapconcat the same as mapcar but concatenates the results into a string starting string with the last parameter
  • identity function which returns the same elements. It’s useful as mapconcat runs a function to retrieve elements and as our elements will be strings, there are no modifications, so identity just returns them. It’s just a filler as function requires a function to return elements.
  • car, cdr and last are list processing functions. car - returns first element, cdr - everything except first. last speaks for itself.
  • concat concatenates passed strings into one string
  • substring returns portion of passed string from start to end indexes. If end index is negative it removes towards the end so in our case 0 -2 removes the , we have just added previously when mapcared the list to have “item”=item, …
  • delete-region deletes region. With this call we remove our selected region
  • insert this insert content into a buffer at cursor location. In combination with delete-region it removes our selection and replaces with a new one

In a few words our function:

  • extracts our selection
  • builds a list variables with each item in a string transformed into "item=", item,
  • grabs all the elements from variables except for last and concats them into one string
  • appends the last element with the last , removed
  • replaces selected region with a new string

In the beginning I noted this is unoptimized - when we grab every item except last I don’t know the existing function so I just reverse the list, so the last item is now the first then run cdr to grab everything except it and reverse back.

Now as I evaluate this function I may want to log out my event object along with mouse coordinates so I just type:

1
event mouseX mouseY_

and my cursor is at _ then I C-SPC C-a to mark the whole line then M-x lua-log and voila. Now of course this method can be bind to some key combination but I believe I shouldn’t waste key bindings for such a seldom task so M-x pattern way works especially when you can type in a pattern like lulo and if it doesnt collide with another similar named function it will be selected.

Emacs: the Returning

So I have started trying to use Emacs again after like half a year after my first try. And this time it’s a pleasure. I believe I needed more different approach.

Note that for each plugin you should read it’s documentation about how to set up it properly for your needs. It will probably cost you a few hours to set things up properly but I amortized the cost between different days, adding more stuff each day and also getting used to the stuff I have already added.

What have I done differently

What I have installed immediately by searching over the internet to supply me with similar to sublime features

  • Switch control key to caps lock key
  • Melpa package repository
  • Helm
  • Projectile
  • Helm-projectile

And my emacs life was never the same. With only these few modules Emacs immediately became almost as efficient as Sublime text for me.

Keys

  • C-x is hold control and then press x.
  • C-x C-x means hold control and press x, release control and repeat. Or you can hold control and hit x twice!
  • C-x s means hold control, then hit x, release control and hit s
  • M-x means alt key and then x key.

Caps lock

I have read about switching control key to caps lock on a lot of websites and decided to give it a try. It really helps to use Emacs as my pinky sits on caps lock naturally. And I don’t feel awkward.

Melpa

Melpa is a repository for Emacs packages. It contains a big list of packages and needs a mention here.
Add this to your .emacs and never turn back:

1
2
3
4
5
(when (>= emacs-major-version 24)
  (require 'package)
  (package-initialize)
  (add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)
  )

You can view packages with M-x list-packages or install immediately with M-x install-package. Package manager lets me to try a lot of different plugins immediately and grep through some keywords I am interested in. For example I am interested if something comes with *undo*in package name, so I M-x list-packages then C-s and search for my query.

Undo/redo copy/paste and cancel things

One major thing which I was missing was C-z and C-y for undo/redo. These standart keybindings are available in CuaMode but I was headed for a challenge. Also Cua mode lets you have standart copy/cut keywords as standart Emacs copy is M-w and cut is C-w and paste is C-y.

Emacs has your back with C-x u to undo. Emacs undo/redo tree is kind of different from usual applications and needs time to get used to but the good thing is - you can almost always revert to a previous state.

C-g cancels current command. In case you messed up some Emacs chord or launched not wanted command C-g and it’s canceled.

If some annoying buffer opened and you want to get rid of it hit C-x k to kill it or C-x <left> or C-x <right> to switch to previous or next buffer. C-x b opens you a list of buffers you have and you can enter a pattern.

To redo simply cancel redo. How so? Example:

1
2
3
4
i say
hello
emacs
_

My cursors is _. Now C-x u moves cursor one line up to the end of the line, another C-x u removes emacs line and so forth. Now what if I want to redo? Then C-g and each C-x u will now redo. To master this in Emacs I highly recommend reading a manual as it’s quite hard to understand without some experimentation and a manual.

Helm ?

Helm changes Emacs popups with more appealing and interactive ones of Helm. I do not underestimate Emacs but Helm really improves experience. Upon quick configuration by guide I can:

See interactive window as I type M-x

This lets me type in a pattern for a command I am looking for. Need I sort lines but don’t remember the command? “M-x sor li” and in the results I immediately see possible candidates:

Interactive occurrence of a pattern

helm-occur differs from Emacs native occur command so that it’s interactive meaning when I navigate with keys through results helm automatically focus Emacs to show the cursor on appropriate line instead of switching to results frame and hitting RET on wanted occurrence.

Projectile

This is simply a must plugin for working on a project. You can set the project by putting a .projectile into your project. In the file you can also add files or directories to ignore for projectile.

Projectile works with helm and there are only two functions I use. Bind them to any key you want:

1) helm-projectile if not in a project it asks you to open a project and shows the recent one. If the project is opened it lets you enter a pattern and interactively shows you matched files in your directory! (Sublime text users C-p)

2) projectile-grep greps through the project for a pattern. I set it to C-c g. If I want to search in certain files I do C-u C-c g and then after typing in a pattern I can type in a pattern for files like *.lua.

Marks

When interacting with a buffer it’s really often needed to bookmark certain parts to jump quickly later. For this functionality Emacs has mark ring.
C-x C-x to set mark
C-u C-x to iterate through mark ring.
It’s a simple killer feature which can be learned to be used in under a minute.

Imagine you have a big file and you just hit some interesting place and you quickly need to jump to the head of the file and jump back. You do C-x C-x then M-< after you look for your file header C-u C-x and you are where you have just been.

Rectangles

Rectangles allow to work with rectangular areas of the buffer. This at first seemed like a useless feature. Who works on rectangles..? It seems I work on rectangles. A LOT. You can add test, replace text, delete rectangular area and so on.

Example:

1
2
3
4
5
function demo()
local a = 1
local b = 2
local c = 3
end

I want to indent those local variable declarations using rectangles. Put the cursor on l on first local. Then C-space to start selecting, hit down two times so it looks like this:

and now C-x r t to replace rectangular area with text. It will prompt you for a replacement. Our rectangle is area between selection start and end. Type in two empty spaces and RET. Bam:

Now I want to remove those local strings. I put my cursor on first l letter of first local definition. Hit down arrow twice and M-f to move cursor one word forward. I got this:

Now C-x r d to remove rectangular area and those local definitions are long gone.

C-x r c lets you fill in a rectangle with spaces. Not delete like C-x r d but replace with space symbols.

Another neat feature I just found is C-x r N which puts numbers increasing from 1 to each rectangular area start position. In our demo case if we again select first l letters of three local definitions and hit C-x r N we see this:

1
2
3
4
5
function demo()
  1 local a = 1
  2 local b = 2
  3 local c = 3
end

I like this feature as it shows me how dynamic Emacs is not that I use it a lot.

Multiple cursors

It’s mc library. It provides plenty of functions and I use only three with custom key bindings:

  • C-> mc/mark-next-like-this adds the cursor on the next occurrence of
    current selecion
  • C-< mc/mark-previous-like-thisadds the cursor on the previous occurrence of current selection
  • C-c C-< mc/mark-all-like-this adds cursors on all the occurences in a buffer

Example time:

1
2
3
function capitalizeName(name)
  return name.uppercase
end

Put the cursor on n of name in arguments then C-space M-f to select the whole word and C-> and watch as both name occurences are selected. If you type in anything it replaces both words with a new input. However sometimes I don’t want to rewrite whole occurrences so C-g cancel selections and leaves only cursors set. Hit C-g again to cancel additional cursors and leave original one.

Screen layouts

I always used to split screen at least vertically to have 2 windows when coding. So I searched for the same functionality in Emacs and it’s of course - a built-in feature. To cut a long story short these are the default key-bindings:

  • C-x 1 make current buffer the only one visible and stretch to fill the whole window
  • C-x 2 split frame horizontally
  • C-x 3 split frame vertically
  • C-x 0 kill current frame

To switch between windows C-x o. It’s a sad thing it switches in order and you don’t have a way to define which exactly window with an arrow key or so. The commands split the current frame, so in case you split once vertically and again vertically You have |AB|C| layout. To auto resize all the frames C-x +.

Better news - windmove. I have only this line in my .emacs:

1
(windmove-default-keybindings 'meta)

And what it allows me to do is M-arrow to switch between window frames. The good part it is quite self aware and intuitive so a must have plugin.

Killer feature about frames I found is registers. C-x r w _ means write window configuration to register where _ is a character corresponding a register for example a. Loading data from register or jumping is C-x j _ where _ is the name.

Imagine a situation - you have window split into 4 parts opened different files and doing some work. For a minute you want to stare at one file in fullscreen. So C-x o until you hit the needed buffer C-x 1 to make it fullscreen and now you need again to split windows, jump to buffers, etc. Don’t worry! Once you want to get rid of the layout temporarily hit C-x r w a then navigate to buffer you want C-x 1. Once you have finished and want that layout back C-x r j a and they are back. This is such a good feature that Emacs immediately became instantly a lot better.

Themes

There are so many emacs themes. Also available in package list. You can choose any you want and modify each of them as you want. There’s only one negative thing I noticed about most of the themes - they cause my Emacs to crash and I am sure it’s because of themes. Despite that fact some themes are very awesome and you can easily switch between them with M-x load-theme.

More cool features I have found

Neat features I have found while randomly wandering around the internet.

undo-tree

A cool plugin/mode you need to enable to get usage of. It shows Emacs undo-redo ring in a tree way which looks kinda cool but I am not used to it yet. Look at it:

Where x is the current position in a tree. You can choose the branches and if you choose a branch and do more changes it makes another branch so basically you can visually navigate through the tree with arrow keys and immediately see the changes in your active buffer.

Abbreviations

This is what I fell immediately in love with. Abbreviations are like your own corrections which Emacs handles for me. I often type in “log:warn()” in our codebase to log the data in Lua but I often mistype “log” with “lgo” out of rush.

There are two places where one can put in abbreviations. Those two places are: global scope and mode scope. For example I don’t want my “lgo->log” abbreviation to work everywhere. I want that only in lua major mode. That’s what add-mode-abbrev is for and add-global-abbrev is for global scope.

Note! Abbreviations only work when abbrev-mode is enabled so be sure to M-x abbrev-mode. Keys for abbreviations:

C-x a + add-mode-abbrev
C-x a g add-global-abbrev

Example time!
Type in logX where X is the cursor. C-x a g and in the prompt type in lgo. From now on once lgo is typed and a space, return, tab, or any other non character input comes Emacs changes lgo into log. Neat!

Forgot what are the key bindings for a function?

M-x describe-function prompts you for a function name and in the new buffer shows you attached key binding if one exists.

Want to know information about some variable?

For example I ran Emacs on Windows and didn’t know where my .emacs file is stored.
M-x describe-variable and type in user-init-file. Bam:


user-init-file is a variable defined in `C source code’.
Its value is “c:/Users/Lukas/AppData/Roaming/.emacs”


Now I want to open that file without touching my mouse because Emacs?

I can C-x C-f or simply hold C then press x and f and I can type in the path to that file.

I am really fancy so I will switch to that window with C-x o then M-< to go to the beginning of the freshly opened buffer, put my cursor on the second line, then C-3 M-f to move my cursor 3 words forward, C-space C-e to start selecting and go to the end of the line and M-w to copy that file name. Now C-x C-f and I have file access prompt ready but it’s filled with my current path? C-a C-k C-y to go to the beginning of the line and kill the whole line forward and C-y to “yank” or paste our selection. Now the problem is that we have pasted with quotes around the text. So C-a del and C-e backspace to remove those. RET. Wow my .emacs file is opened.

Man that was a chore. I bet any 5th grader with a mouse would do that faster. If you hate the manual stuff as much as I do read forward. We will simply define an Emacs command or record a macro(later post on those). Emacs uses elisp but for combining several commands it’s pretty easy to learn.

Like how hard it is to write simple elisp code? In this case I want a function to copy contents between two double quotes on the current line my cursors is. Look:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(defun copy-in-quotes()
  (interactive)
  (let (p1 p2 line result found)
    (setq p1 (line-beginning-position))
    (setq p2 (line-end-position))
    
    (if (= p1 p2)
      (message "Empty line!")
        (setq line (buffer-substring-no-properties p1 p2))
        (setq found (string-match "\".+\"" line))
        (unless found)
          (setq result (match-string 0 line))
          (message result)
          (kill-new result)
        )     
    )
  )

Now If I again describe-user-variable and enter user-init-file move my cursor on that line, call this function and path is copied into my kill-ring. With a simple (global-set-key (kbd "C-c q c") 'copy-in-quotes) I can bind this function to C-c q c key binding. Note that when binding your own key bindings use C-c not C-x as C-x is default Emacs key binding so you may overwrite some good stuff.

Let me run through a brief explanation of what we did here:

  • (interactive) means our function can be called via M-x name. Simple as that.
  • (message variable) outputs the variable value in minibuffer area as I want to display what we have copied.
  • (kill-new line) actually doesn’t kill anything. This function simply puts the value in a kill-ring*.
  • let allows me to have some variables defined and used in scope. Form is (let (a b c) (use-a-b-c)).
  • setq sets value to the variable so works well in conjunction with let.
  • buffer-substring-no-properties p1 p2 returns content with no additional information from buffer between two points p1 and p2.

I have mentioned kill-ring. In Emacs you actually don’t delete stuff or erase it. When you kill a line it doesn’t disappear. It exists in a kill ring. By removing one symbol at a time with DEL or BACKSPACE it doesn’t go to kill ring but for example C-x k which kills the whole line forward with respect to the cursor - does.

run this thing

Put the cursor after the last parentheses ) and hit C-x C-e to evaluate elisp code. Cool thing is - you can evaluate anywhere you want. I write some Lua code and want to quickly divide 8889 by 45? I type (/ 8889 45) hit C-x C-e and see the result at the bottom. Note that to cast result to float at least one operand needs to be a float so 8889 would be 8889.0 or 45 should be 45.0 as integers are promoted to floats when in doubt.

Now as we have a function evaluated and no buffer opened with error message we can see functions name at the bottom our function name meaning Emacs got it. It has our new function. Now again: M-x describe-variable type ‘user-init-file’ then M-< arrow-down and M-x copy-in-quotesand now the strings between quotes is copied into the clipboard.

If I want to have this functionality always as I use Emacs, I need to open .emacs file again. As I haven’t killed it’s buffer previously with C-x k I can simply C-x b ema RET and buffer with ema pattern in its name opens. So .emacs is now opened. M-> go to the end of the file then copy the function definition and key binding definition C-x C-s to save.

As we have edited .emacs file we need to evaluate new code. We can:

  • restart Emacs.
  • M-x load-file .emacs to evaluate elisp file.
  • evaluate by hand each definition with C-x C-e.

Extra

Where to search for information about Emacs? Official website is a mind blowing resource describing every bit of Emacs and Elisp. It’s such a good documentation it deserves some kind of a medal.

For the last - IRC part I need to mention that all of you have heard a joke that Emacs is not just a text editor. So you want some quick help on IRC so you go to download IRC client or. No no no. Stop. M-x rcirc and see as it automatically connects to freenode irc server by default with a random nickname. /join #emacs and ask your question! People there are quick to respond and got me a lot of useful asnwers.

Conclusion

I was in doubt if I should begin again my journey through emacs and “waste” my time on it. I always though that it’s quite useless to invest into such an editor as I though that one spends more time thinking about code than writing it.

The light hit me once a coworker told me that vim made him navigate through the project more smoothly. I changed my perspective that a decent editor lets you dive into a project with more pleasure and maybe more easily. Currently I miss some features of sublime text which I believe can be done with some elisp so later on I will write more in depth guides.

This post is not about teaching about Emacs or anything like that as I just mashed stuff from the tip of my tongue in one evening. With this post I wanted to show that Emacs is not some evil magical thing which is very hard to get started. Also I tried to show that Emacs is a pleasure to extend.

Reflections and Refractions

When I have made a raymarched sphere I was quite happy. But I wanted to make a glass one. So the first experiment was to make a sphere which only worked like a lens. Just to see what’s behind it with a bit refracted rays.

I shoot a ray from the eye adjusting it for the current pixel with
vec3 ray_dir = normalize(up * uv.y + right *uv.x + forward);
Where right and forward are just static vectors and uv is my current pixel. Like this:

1
2
3
vec3 up = vec3(0.0, 1.0, 0.0);
vec3 forward = vec3(0.0, 0.0, 1.0);
vec3 right = cross(up, forward);

So now once my ray hit the ball, I didn’t take the pixel from the texture and display it but I have used a cubemap. First for the refraction I am happy GLSL handles it internally as well as reflect(), I just need a vector and a vector I am going refract on and of course a coefficient. For the cubemap there is textureCube() instead of texture2D(). So the whole magic happens here:

1
2
vec3 refracted_ray = refract(ray_dir, -point_normal, refract_koef);
vec4 refracted_color = textureCube(iChannel2, refracted_ray);

I use -point_normal because I refract the ray going from me instead of going into me that’s why I invert it and refract_koef is 1.02. Refraction coefficient only needs to be above 1 to be a convex lens and below 1 to be a concave lens. Now for the rays that don’t hit anything I need to draw a normal pixel from the cubemap:
gl_FragColor = textureCube(iChannel2, ray_dir);

The pseudocode is this:
1
2
3
4
5
6
7
8
9
...
float dist = raymarch(...);
if (dist < max_distance) {
  vec3 refracted_ray = refract(ray_dir, -point_normal, refract_koef);
  vec4 refracted_color = textureCube(iChannel2, refracted_ray);   
} else {
  gl_FragColor = textureCube(iChannel2, ray_dir);  
}
...

And the result looks good:

I wanted to push this lens a bit forward and make it look like a sphere - so reflections too. For this I simply need to reflect the ray, get the pixel from the cubemap and mix reflection and refraction colors. Reflection is even more easy. Now point_normal is positive because it points my the viewers eye.

1
2
vec3 reflected_ray = reflect(ray_dir, point_normal);    
vec4 reflected_color = textureCube(iChannel2, reflected_ray);

And I mix the colors with gl_FragColor = mix(reflected_color, refracted_color, 0.5);. This shows up:

It reflects rays and also refracts but it isn’t realistic as I have hardcoded a constant 0.5 so refraction and reflection rays are combined as equals while in reality the more straight I look into the object the more refraction I see and less reflection while on the other hand when I look into the object from the side almost all I can see are - reflections. To fix this I have found fresnel lens algorithm on GPU Gems 2. So we simply need to get coefficient between reflection and refraction and it looks ok when written like this:

1
2
3
4
float fresnelBias = 0.00;
float fresnelScale = 0.25;
float fresnelPower = 1.97;
float mix_coef = fresnelBias + fresnelScale*pow(1.0 + dot(ray_dir, point_normal), fresnelPower);

The constants are hand tweaked because I didn’t find any suitable ones. So consider this an experiment. So now my color is calculated with this:
vec4 mixed = mix(reflected_color, refracted_color, mix_coef);;
And the result looks more realistic:

The last thing which I wanted to do - a rainbow on a bubble. This is due to the different refraction coefficient of colors, so to implement this I refract R, G and B components of a texel differently. Again - the coefficients are hand tweaked:

1
2
3
4
5
6
7
8
9
10
vec3 etaRatioRGB = vec3(1.03, 1.06, 1.09);

vec3 TRed   = refract(ray_dir, -point_normal, etaRatioRGB.r);
vec3 TGreen = refract(ray_dir, -point_normal, etaRatioRGB.g);
vec3 TBlue  = refract(ray_dir, -point_normal, etaRatioRGB.b);

vec4 refracted_color;
refracted_color.r = textureCube(iChannel2, TRed).r;
refracted_color.g = textureCube(iChannel2, TGreen).g;
refracted_color.b = textureCube(iChannel2, TBlue).b;

And the result looks bubbly!:

In conclusion - it was really fun and quite fun and fresnel lens formula really makes sense. From my understanding as ray_dir and point_normal are normalized the dot just gives me a cos of those vectors so when I look forward into the bubble I get 1 which maximizes refractions I see and when I look from the very side into it all I can see are reflections because of cos giving me zero. The pow there exists because of inverse square law or I believe so.

For the bonus here is the concave lens with refract_koef = 0.98;:

You can see the code and tweak it also online here:
Lens on Shadertoy
Bubble on Shadertoy

Voronoi Diagram

Voronoi

Voronoi diagram is a method for dividing space into a set of regions. We have a set of points and each region is a container of points/pixels closer to that exact point than the others.

A quick play on Shadertoy shows me that it’s easy as it sounds. Of course it’s inefficient and complexity of this implementation is O(N^2) because of loop going through all the points and searching for the closest one. This is the result:

I found this algorithm to be very promising for procedural generation for example for shattered glass or these random examples. I will of course try to build something nice with this thing.

Glsl Raymarching

My second part about GLSL is about raymarching - a simple method to draw figures by shooting “rays” all over the field of view and if the ray collides with an object, we draw something like a pixel or like a lighted pixel from a texture.

Tricks

The first trick of ray marching is that when we shoot the ray, we increment it and to squeeze better performance we do not increase it by a fixed rate but rather by a dynamic one. The question is - how dynamic? The trick is to get the distance of the nearest object and move that amount forward because we are sure the ray will not collide with any object in that path.

My approach

My approach is a very basic and simple one. I simply want a ball in the middle of the screen defined only by it’s radius and nothing more. Making ray collide with a circle is kids play.

Lets begin

So of course I have my circle radius, let’s call it R. #define R 0.3
Then I want to normalize my pixel position into [-1..1] range, so:

1
vec2 uv = ((2.0 * gl_FragCoord.xy) - iResolution.xy) / min(iResolution.x, iResolution.y);

Next thing we need I believe is the distance function to our circle. Now distance to the circle is probably the easiest task :)
1
2
3
float get_distance(vec3 point) {
  return length(point) - R;
}

Here we accept our 3D point vector and we also assume it’s in the center of the screen. length is a built-in of GLSL no need to write it ourselves. Actually now, we are almost there to draw a circle :). We have our circle defined, the distance function, we need only camera/eye vector and do the rays.

Define our eye

Lets say I want to be viewing directly to the center but from some distance. This means I need to modify only Z value.

1
vec3 eye_pos = vec3(0.0, 0.0, -3.0);

Eye position alone is not enough so I have to define where my eye is looking
1
vec3 forward = vec3(0.0, 0.0, 1.0);

My eye is looking forward. Actually it doesn’t matter the exact number of Z part, it can be anything but the vector needs to be normalized in order to get correct results. We can simply pass in 3.5 instead of 1.0 but then we should wrap it into normalize().

Turn on the lights!

Lets define the light.

1
vec3 light = vec3(0.0, 0.0, -3.0);

Our light will be at the same place where our eye.

Rays

We now need to shoot rays all over the scene to detect the object. We define the up vector. It will be used as a helper by doing cross product of it and forward vector.

1
vec3 up = vec3(0.0, 1.0, 0.0);

And we already have the forward vector(direction of where our eye is looking). We can’t easily calculate each ray’s vector so we defined the up vector in order to get the perpendicular vector - a cross product.
1
vec3 right = cross(up, forward);

Now this right vector is pointing perpendiculary to our eye direction and up vector. We simply adjust it a bit and get a vector to shoot(so we would not shoot always to the same spot).
1
vec3 ray_dir = normalize(up * uv.y + right*uv.x + forward);

And here we go. For each pixel going from -1.0 to 1.0 we calculate the ray vector. Let me explain the reasoning - we take the up vector multiply it by y coordinate of the pixel to adjust the vertical shift of our ray then we multiply our right vector by pixel’s x value to adjust horizontally. Now we squash those two terms together and add a forward vector. There may be a question - why? It’s just to point the ray to the direction where we are looking.

Ray march it

Finally the last lego part we need is ray marching function. This function will return the distance if the object was hit. If it fails to find any object for a hit it returns a maximum distance so we could draw something like a black background or a sky or a wall.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define MAX_STEPS 100

float raymarch(vec3 ray_origin, vec3 ray_direction) {
  float d = 0.0;

  for (int i = 0; i < MAX_STEPS; i++) {
    vec3 new_point = ray_origin + ray_direction*d;
    float s = get_distance(new_point);
    if (s < epsilon) return d;
    d += s;
    if (d > max_distance) return max_distance;
  }
  return max_distance;
}

Let’s break down the things. First we define the maximum of steps to raymarch. The bigger the number - the better quality of a scene for the price of performance. Our function accepts ray origin point and it’s direction. Since we project the image into an “eye” so it is our ray_origin and direction is ray_dir which we have calculated just above.

So now the drawing goes like this:
1
2
3
4
5
6
7
8
9
...
float d = raymarch(eye_pos, ray_dir);

if (d < max_distance) {
gl_FragColor = vec4(1.0);
 } else {
gl_FragColor = vec4(0.0);
}
...

Now we simply get a white circle in the middle of a screen with black background.

Lighting

It’s pretty easy to make a feeling of 3D as we have a distance returned. The lighting works simply by taking cosine of light vector and surface normal(vector pointing up with respect to a point). For this operation we need to get a normal vector of our point.

1
2
3
4
5
6
7
vec3 get_normal(vec3 point) {
  float d0 = get_distance(point);
  float dX = get_distance(point - vec3(epsilon, 0.0, 0.0));
  float dY = get_distance(point - vec3(0.0, epsilon, 0.0));
  float dZ = get_distance(point - vec3(0.0, 0.0, epsilon));
  return normalize(dX-d0, dY-d0, dZ-d0);
}

The trick to get point’s normal in our case is to calculate distances to the points that are farther backwards in all directions by just a tiny bit. Then we take the differences and construct the normal. And so:
1
2
3
4
5
6
7
8
9
10
11
...
vec3 ray_dir = normalize(up * uv.y + right *uv.x + forward);

float d = raymarch(eye_pos, ray_dir);
if (d < max_distance) {
  vec3 point = (eye_pos+ray_dir*d);
  vec3 point_normal = get_normal(point);
  vec3 light_dir = -normalize(light-point);
  float dotp_diffuse = max(0.0, dot(light_dir, point_normal));
    gl_FragColor = vec4(dotp_diffuse);
...

The dotp_diffuse is dot product of diffusion(direct color of light on the circle). As the vectors are normalized it’s simply a cosine of light vector and surface normal. If the light hits the surface at 0 angle it will be 1.0 if they are perpendicular it’s 0.

To paint the circle in a color I need to simply multiply color vector by dotp_diffuse, like so vec4(0.0, 0.0, 1.0, 1.0) * dotp_diffuse. This gives us a blue shaded circle:

More fun

We can do more fun with this circle. Like bump map it. In our case we can simply adjust the get_distance() function to return a bit distorted distances by a noise(from a noise texture on iChannel1. It’s Shadertoy thing).

1
2
3
4
5
6
7
float get_distance(vec3 point) {
  float bump = 0.0;
  if ( length(point) < R + bump_factor) {
    bump = bump_factor * texture2D(iChannel1, point.xy).r;
  }
  return length(point) - R + bump;
}

Here’s cheap bump mapping:

Now note that I chose the incorrect method for getting a point from a texture as it’s a circle and I directly access it without any transform. For this reason I borrowed this technique from Inigo Quilez. And here it is:

1
2
3
4
5
6
vec4 texture3d (sampler2D t, vec3 p, vec3 n, float scale) {
  return
    texture2D(t, p.yz * scale) * abs (n.x) +
    texture2D(t, p.xz * scale) * abs (n.y) +
    texture2D(t, p.xy * scale) * abs (n.z);
}

I pass in the texture, point, point normal and scale. I will show it’s usage later.

Attenuation

A thing to fix would be - color attenuation. The farther the distance, the more dimmed the color.

1
float attenuation = 1.0 / (1.0 + K*pow( length(light - point), 2.0));

It works by inverse square law - color intensity minimizes squarely as distance increases.

Specular lighting

We have diffuse lighting and there’s a specular one. We need a vector of the reflected light and GLSL has a built-in called reflect(). So we can simply do:

1
vec3 reflected_light_dir = reflect(-light_dir, point_normal);

Not to forget the ambient light. If it’s not pitch black we should have some ambient color. I am not going to calculate it and simply will hardcode it #define ambient 0.4 and add it to the final color vector.

Here’s textured sphere with very small and rare bumps and diffuse, specular and ambient lighting.

Plane

I would like to have a shadow. But to cast a shadow I need to have a surface to cast it on, so lets assume we will have an infinite plane. A shorthand to check if point is on a plane - dot(point, plane_normal) - elevation. As I want to have floor, it becomes dot(point, vec(0.0, 1.0, 0.0)) - 1.0. If elevation would be zero - the plane would be invisible to our eye.

I am not going to separate it into a new function but will simply adjust get_distance() to calculate if it hits the ball or a plane.

1
2
3
4
5
6
7
8
9
10
11
12
float get_distance(vec3 point) {
  float bump = 0.0;
  float elevation = -1.0;

  if ( length(point) < R + bump_factor) {
    bump = bump_factor * texture3d(iChannel1, point, normalize(-point), 0.5).r;
  }
  return min(
    length(point) - R + bump,
    dot(point, vec3(0.0, 1.0, 0.0)) - elevation
  );
}

Here we can also see the usage example of texture3d(). get_distance() now returns the minimum of length to circle or a plane. Which one is closer - that one is hit of course.

Shadow

To cast a shadow I do it like this: first when I know the ray hit a point(a plane or a ball because shadow can be on both) I take that point and raymarch again by using that collision point as source point and direct it to the light. Now if the ray did collide with something along the path it means we should cast a shadow. Like if it’s a plane or a ball and just above the ball there’s a light. Then the area on the bottom of the ball(on the plane) will shoot rays to the light and hit ball and so we have a shadow.

We will make a soft shadow without sharp edges.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
float shadow_sample (vec3 org, vec3 dir) {
    float res = 1.0;
    float t = epsilon*20.0;
    for (int i =0; i < 100; i++){
        float h = get_distance (org + dir*t);
    if (h <= epsilon) {
            return 0.0;
    }
        res = min (res, 32.0*h/t);
        t += h;
    if (t >= max_distance) {
          return res;
    }

    }
    return res;
}

Lets breakdown this thing. We have t which is increased by some constant. It’s because get_distance() might get the same point we’re shooting from since the starting point is near ball/plane. A value of 20.0 in this case works for me since lower values cause visual artefacts I can’t explain :-).

Within a loop we get distance h. If it hits something(checking approximately with epsilon) then it’s a pitch black. Otherwise we set res to be min of itself(since we don’t want to loose it’s accumulated value) and distance divided by step - “shadow step”. 32.0 multiplier is there for shadow softness - the higher the value the harder the shadow.

We put in the shadow into equation by multiplying everything by shadow_sample(point, -light_dir). And we get kinda huge and ugly equation which begs for a refactoring. But for experimenting purposes it does it’s job. Final result can be looked at and tweaked here: link.