Tuesday, June 23, 2015

Deferred irradiance volumes

I have some sort of deferred irradiance volumes based global illumination already up and running but there are several caveats I don't really like about it. Let me first show you some screens from my current implementation so can eventually see what difference it makes ( or doesn't ) for indoor scenes. In fact, there are probably much suitable scenes to demonstrate the strength of GI, but then again - this is what I have and this is what is going to be in the final game ( or something very similar )

To be honest, I'm not really sure if such a small difference is going to be notices by the end users and appreciated. A side observer could exclaim : "Man, no one will ever notice that global illumination your are trying your best to achieve, especially if you do not provide direct, side-by-side comparison  as in the pictures  above. People just want a game to be fun, engaging and running at decent speeds on their computers. No one will appreciate a GI solution that make almost no difference to the final picture, but stall their computers as hell."
Anyway, GI is cool and is the next big thing in computer graphics( realtime).

Preprocess :

The above technique looks like roughly like this :
  • Spread light probes all over the place. A regular grid will do.
  • Gather incoming light by rendering a cube map and store it as SH coefficients

At render time:
  • Render deferred lights as probes and for every affected pixel they cover, sample SH with a normalize(pixelPos - probePos) normal. 
  • Add the value as indirect light to the pixel color value.
Well, this obviously works but :
Is semi static. Dynamic objects can sample probes just fine and receive correct lighting, but environment and lighting cannot change without recomputing nearby probes again.
Is kinda slow. To get decent results, many probes must be present on that location. Still, a probe is just (third order) 3 * 9 floats and probes exists only where needed to contribute. Empty space( if no object can enter, including dynamic ones) or space outside the L-shaped level do not probes to be present. A 3D texture on the other hand covers entire box with data, no matter if there is something or not.
Unfortunately, many probes stuffed together to make a dense grid means lots of overdraw -> slow
Also, probes aren't geometry aware so expect lots of light bleeding.

I think I'm going to try some kind of Light propagation volumes approach with volume textures that move along with the camera. Unfortunately Direct3D 9.0 cannot directly render to volume texture, so I will most probably try the geometry injection pass with locking the slices and using some kind of depth peeling.

Monday, April 27, 2015

Tray Racer ( aka Ray Tracer )

No, the title and the article isn't aimed to whiskey drinking competition with crystal glasses full of ice cubes, slashed from ice trays. It's a silly try for a wordplay, meant to make a reference to ray tracing.
I was asked to write a thesis in computer graphics, so I had to refresh my basic knowledge not only in interactive graphics and rasterization, but also and other methods of non-real time graphics like ray tracing.
I find it quite useful to investigate a completely different approach to computer graphics. And to be honest, I like it very much. It is much more elegant and natural way to render things, not to mention it often needs just a small fraction of hacks needed to render proper graphics on screen, compared to rasterization methods. You just have your eye properties, like position, direction and field of view and for every pixel on screen you shoot rays through the scene to test what objects ( their properties- color, etc.) and where those rays hit them ( lighting interaction calculation). In a very simple test case, you can use spheres to test against your eye rays. Spheres can be represented parametrically by a position in space and a scalar value for the radius. 
You can find the intersection point completely algebraically by substituting the sphere equation in the line equation and solving a resulting quadratic equation. The idea is : if you have your ray equation and sphere equation, you can find solutions ( points ) that satisfy both equations at the same time. That's where ray pierce the sphere. For a ray tracer you are most likely interested in the closest point to the origin of the ray.
I'm playing with a simple ray tracer, that supports reflection, shadows, texture mapping ( sphere coordinates turned into UV coordinates ) and even global illumination by calculating several bounces of light. It uses recursion as it provides more elegant way for solving such problems. It's also single threaded.

Tuesday, January 13, 2015

Mapping the Light

Lightmaps are old tech, they eat up a lot of memory, they are static, they need pre-processing, they are expensive to compute and cheap to run(except the memory cost), but they can look very natural on static geometry scenes especially if they are made with radiosity in mind. Ie, contain light diffuse interreflections between surfaces. Radiosity is a global illumination algorithm in the sense that the illumination arriving on a surface comes not just directly from the light sources, but also from other surfaces reflecting light. Radiosity calculations are viewpoint independent which increases the computations involved, but makes them useful for all viewpoints.
In the times of modern GPU's running modern lighting approaches like deferred lighting, where everything is fully dynamic and per-pixel, speaking about old tech like lightmaps is a bit odd at first sight, but if you think about the increasing mobility of the computing in general and modern hand-held devices that run on limited battery power and physical dimensions, you will see why the old stuff is gaining momentum on new devices.
Recently I went on implementing a simple lightmapper ( without radiosity ). I will outline the process and provide some code on a next post. Here are a few screenshots of how it looks like so far.

Wednesday, August 13, 2014

Dust off the old stuff

When you have nothing new to show, you bring up the old stuff. While you are watching and examine them, you can feel forgotten feelings again and bring back some memories. Also, you can see where you were back then, and where you are now. If the old stuff look very proficient and great to you, there is a big chance you don't have much of a progress in what you are doing. You can even notice a regress. Anyway, the important thing is to set your clock straight and not loosing ground beneath your feet.

Friday, July 11, 2014

Timing Advance Processor with a PIC

Timing advance processors are electronic control units that change the original spark advance of the engine when it runs on CNG or LPG, thus optimising the engine’s operation with these fuels.

Because LPG is a higher octane fuel than petrol the combustion time is longer. It is possible to change the ignition timing mechanically or electronically. This makes the difference between driving on LPG or petrol less. The fuel consumption and the engine power at low speed could be the same or even better than on petrol. Also the engine will run more smoothly and the exhaust emission values will be better.

;Svetlin Spasov
;Basic Timing Advance Processor for LPG and CNG powered vehicles

        LIST P=16F877, R=DEC
#include          ; processor specific variable definitions
      __config _HS_OSC & _WDT_ON & _LVP_OFF  

; Variables

;angle           res     1  
;v_work          res     1
;back            res     1
;up              res     1

ScratchPadRam   EQU     0x20

; Variables

angle    EQU     ScratchPadRam+0
v_work   EQU     ScratchPadRam+1
back     EQU     ScratchPadRam+2
back1    EQU     ScratchPadRam+3
up       EQU     ScratchPadRam+4
up1      EQU     ScratchPadRam+5
store    EQU     ScratchPadRam+6

; Program Code
;   Set the reset vector here.  If you are using a PIC16C5X device, use:
;               ORG     
;   Otherwise, use:
;               ORG     0

                ORG     0       
                GOTO    Start
                ;comp = 3
MORE_THAN macro comp ,var0 , jump
    MOVLW comp ; slagame comp v accumulatora
 SUBWF var0, 0      ; ako v starshia registyr ima po-malko ot comp, obshtoto 4islo ne moje da e po-goliamo ot 1000
    GOTO jump;
                ;comp = 3
LESS_THAN macro comp ,var0 , jump
    MOVLW comp ; slagame comp v accumulatora
 SUBWF var0, 0      ; ako v starshia registyr ima po-malko ot comp, obshtoto 4islo ne moje da e po-goliamo ot 1000
    GOTO jump;

EQ_JP_EQUAL macro comp, var0, jump
MOVF comp;
XORWF var0,0;
GOTO jump;

EQ_JP_NEQUAL macro comp, var0, jump
MOVF comp;
XORWF var0,0;
GOTO jump;

MORE_THAN_16BIT macro compH, compL,varH,varL,jump ; jump to address if more than

      MOVLW varH ; sloj starshia reg v akumulatora
   BCF STATUS, RP1 ; izberi banka
   BCF STATUS, RP0 ; banka
   SUBWF compH, 0 ; izvadi tova i stoinostta v akumulatora
   BTFSS STATUS, Z ; proveri statusa za 0 bit
   GOTO L_setAngle_25 ; jump kym izhoda
   MOVLW varL          ; slovi mladshia v akumulatora
   SUBWF compL, 0     ; izvadi gi sus sravniavanoto 4islo
      BTFSC STATUS, C   ; proveri status za polojitelen ili otricatelen rezultat ot operaciata
   GOTO jump        ; sko4i, ako resultata e polojitelen

; Main Program

                ORG     ScratchPadRam+7

CLRWDT ; ku4eto v kusheto
CLRW ; accumulatora v kire4a
MOVLW b'11111111' ; portb initializaciq
               ; bankata e 0

MOVLW b'00000000' ; kak pishem tuka kato e vhod...


   CLRF TRISC ; izhoda 0
   CLRF PORTC    ; nulirame izhoda
BANKSEL angle 
    CLRF v_work ; same bank as angle, virtualen raboten resistyr
    CLRF angle
    CLRF back ;
    CLRF back1 ;
    CLRF up ;
    CLRF up1 ;

    BANKSEL angle;
    MOVLW b'00000011' ; malko ugal za test
    MOVWF angle ;
    MOVF angle,0;
    MOVWF store ; store angle



    MOVLW b'00000011' ; malko ugal za test
    MOVWF angle ;

     BTFSC PORTB , 1 
     RLF angle, 1 ;   //umnojavame X 2 
     BTFSC PORTB , 2 
     RLF angle, 1 ;   //umnojavame X 2 
     BTFSC PORTB , 3 
     RLF angle, 1 ;   //umnojavame X 2 
     BTFSC PORTB , 4 
     RLF angle, 1 ;   //umnojavame X 2 

    BTFSS PORTB , 0 ; // (if portb.f0 == 0 && v_work.f0 == 1)
    BTFSS v_work , 0 ;
    GOTO chek1 ;
    BCF v_work, 0 ;
    CLRF back
    CLRF back1
    MOVF up, 0     ;zapazvame broaia4a v back
    MOVWF back
    MOVF up1,0;
    MOVWF back1; 
    GOTO setAngle;

    CLRF up ;
    CLRF up1;
    BTFSC PORTB,0; // if portb.f0 == 1 && v_work.f0 == 0
    BTFSC v_work,0;
    GOTO cont;
    BSF v_work,0;
    MOVF PORTB , 0
    GOTO loop; 
   BTFSC PORTB,0; // ako nivoto e visoko, uveli4i broaia4a na visoko nivo
   GOTO proc;

   GOTO loop
   GOTO incUp;  // uveli4i broia4a

  ;DECFSZ back,1;
   CALL decBack;  // namali back broia4a
   ;DECFSZ back,1;
   MOVLW 0 
   XORWF back,0; ako "back" broaia4a stane 0
   GOTO loop;   // mladshia ne e 0 produlji
   MOVLW 0   
   XORWF back1,0
   GOTO loop;  //starshia ne 0, produlji

   BCF PORTC,0; // ako back stane 0 nivoto dolu

GOTO loop;


MORE_THAN_16BIT up1,up, 3 , 200 , L_setAngle_0 ; proverka na oborotite

  BANKSEL angle;
     MOVF up, 0     ;zapazvame broaia4a v "back"
     MOVWF back
     MOVF up1,0;
     MOVWF back1; 

     MOVF up1,0;     // sloji starshia registyr v store
     MOVWF store;    // poneje, veroiatno starshia registyr ne e uspial da se uveli4i dostatu4no pri visoki oboroti
     ;RLF store, 1 ;   //umnojavame X 2 
     ;RLF store, 1 ;   //umnojavame oshte X 2 

     CALL decBack; 
     DECFSZ store,1;  
     GOTO dec11;       // loop


 GOTO retsetAng; / iz4ee !

L_setAngle_0: ; dec back counter

     MOVF angle,0;
     MOVWF store; //store ang > "store"
     ; DECF back,1;
     CALL decBack; 
     DECFSZ store,1;  
     GOTO dec;       // loop
GOTO retsetAng; // iz4ee!

   INCF up, 1
   INCF up1, 1

GOTO retUp;

   INCF back, 1
   INCF back1, 1


decBack: ;it's a func cos it's called more than 1 times, maybe it should be inlined for best speed instead of code size
            MOVLW 1
   SUBWF back, 1
   DECF back1, 1


Wednesday, July 9, 2014

Z-brush staggering

A few years ago, the revolution in the form of Z-Brush-like programs was just gaining momentum. My first contact with a similar approach to 3D modeling was naturally with Z-Brush itself. Pixologic were the big boys in the field back then.
I liked the idea, the workflow felt much more natural and intuitive than the classical method of moving vertices and splitting edges.

This is one of my first attempts to do something useful with it. Unfortunately by the time I had a very slow computer, so it looks a bit like stop motion animation with a clay figure.

Then I made a very simple program that tried to mimic some of the basic Z-brush functionality. It was written in Borland C++ Builder 6.0 and uses DirectX 9.0 for visualization. I still have it somewhere on my hard drive and most probably will post or link the code here if someone is interested.

Friday, June 20, 2014

A small collection of images from the past

I decided to make a post and collect some old videos and screenshots from the game I'm working on.

An attempt to port the engine to OpenGL