Dynamic scene lightning
coal

If you are not tired from me... I want to discuss another shader practice - scene lightning. 

At ideal it will look in editor as: 

1. User drags spot light on scene from palette

2. Set it properties

3. All around is lighted.

I want to use that practice in my game and try to make some demos of what I mean. I will post light demos here - and may be new idea will appear - how to make automatic scene lights in framework. Such simple - that anyone could use it.

Comments 1 to 15 (17 total)
Gio

Hi

Yes it should be pretty simple, I made a demo of it some time ago, you can check it out here

I'm not sure whether it should be built into the core of the engine though. I can see it being useful in some cases, on the other hand though keeping the core framework as small as possible is also important.

I think perhaps we could add the shader code to the list of predefined shaders, so it's easy to select it from the editor, but it also doesn't make the core framework any bigger.

coal

Part 1. God rays

 

I'm very inspired by your demos and study by them. I saw them all, thanks a lot!

I think that js size +/-100Kb has no affect on 100Mb game (textures). Also, you can make "min" version of framework by compacting wade.js in wade.min.js version with tools.

have some ideas on lightning, than don't affect framework size, but firstly I must study technics by myself...

Here is first demo - "god rays". Just postprocessing. The only thing to do in framework - add postprocessing option for all layers if possible, currently it is impossible, only for each layer

http://seganelservice.ru/WebGL/Wade18/ - you can refresh trees by click

It is also little slow because of 100-150 trees shaders, and 4000*4000 sprite of moonglow. Particles will solve this problem, and I'll try to optimize moonglow

coal

Part 2, Еffect "LightGlow", with environment glowing. Effect can be applied to any layer, it imitates any light

http://seganelservice.ru/WebGL/Wade19/ - god rays + glow + original 

http://seganelservice.ru/WebGL/Wade20 - only god rays + glow, only light effect without original picture

Now glow moved to posteffect from sprite. Here is posteffect code with two effect in one shader. Only two uniforms: float lightPositionOnScreenx and float lightPositionOnScreeny

Shader needs to be worked a little for private needs, ready at 95%

//blending params
bool moonGlowEnabled = true;
bool godRayEnabled = true;
bool realEnabled = true;
float realColorIntence = 0.8;//real color mix intence

//glow params
float kResolution = 1080./1920.; //screen resolution koef
float nearRadius = 0.; //cut moon radius from glow if need
float farRadius = 0.2; //far radius of sky glow if need
//float farFade = 2.0; //fade koef after farRadius
float kGlowDistance = 0.7; //glow distance on screen
float glowIntence = 0.2; //intence of glow
float blueIntence=5.0; //blue gradient intence
float yellowIntence=1.3; //yellow glow intence


//godrays params
float decay=0.96;//0.96815
float exposure=0.28;//0.09
float density=0.5;//0.326
float weight=0.1;//0.38767
int NUM_SAMPLES = 100; /// NUM_SAMPLES will describe the rays quality, you can play with



vec4 realColor = texture2D(uDiffuseSampler, uvAlphaTime.xy);
vec2 originaluv =uvAlphaTime.xy; 

if (godRayEnabled)
{
    vec2 uv = uvAlphaTime.xy;
    vec2 deltaTextCoord = vec2( uv - vec2(lightPositionOnScreenx,lightPositionOnScreeny) );
    
    deltaTextCoord *= 1.0 /  float(NUM_SAMPLES) * density;
    float illuminationDecay = 1.0;
    
    for(int i=0; i < 100 ; i++)
    {
             uv -= deltaTextCoord;
             vec4 sample = texture2D(uDiffuseSampler, uv );
    	
             sample *= illuminationDecay * weight;
    
             gl_FragColor += sample;
    
             illuminationDecay *= decay;
     }
     gl_FragColor *= exposure;
}
 
//mix real picture
if (realEnabled)
{
    gl_FragColor = realColor*realColorIntence + gl_FragColor;
}

//moon glow sky gradient
if (moonGlowEnabled)
{
    vec4 color = texture2D(uDiffuseSampler, originaluv);
    vec2 uvmoon = (uvAlphaTime.xy - vec2(lightPositionOnScreenx,lightPositionOnScreeny)) / kGlowDistance;
    uvmoon.y*=kResolution;
    float radius = length(uvmoon);
    if (radius>nearRadius) //radius cut if need
    {
        color = vec4(yellowIntence,yellowIntence, radius*blueIntence,1.0)*(1.-radius)*glowIntence;
        if (radius>farRadius)
        {
            if (radius<1.0)
            {
                gl_FragColor = color + gl_FragColor;
            }
            gl_FragColor = gl_FragColor/(radius+(1.0-farRadius));
        }
        else
        {
            gl_FragColor = color + gl_FragColor;
        }
    }
    
    
}

 

coal

Now I need shader uniform of type "Array of float" or "Array of vec2". Please add this in editor.

In this uniform I will set not one light position, but 10,20,30 lights on scene. And work all of them in one postprocess shader with the same speed.

P.S. Also, you can add other uniform typed arrays and also type bool. Not important, but could be useful.

P.P.S I want to notice, that shaders are extreme fast. There are 1920*1080=2073600 pixels on screen and 2073600 calcs in GPU every frame - no differ how much objects. So, speed is the same for any effects count. Shaders and GPU calcs are the key to speed. So, editor needs some little work at that area (shaders)

coal

Part 3. Flying glowworms

This demo explain why I need such array. Now I have 6 uniforms for 2 lights and some duplicate code in shader, but... What if 10 lights?

http://seganelservice.ru/WebGL/Wade22/ without god rays

http://seganelservice.ru/WebGL/Wade21/ with god rays

Also I strongly need one postprocessing for all layers one time. Currently I can't make lightning in game if I have many layers. Or I must duplicate shader code on each layer.

coal

>Also I strongly need one postprocessing for all layers one time. Currently I can't make lightning in game if I have many layers. Or I must duplicate shader code on each layer.

 

Why I need that? I found, that all scene lightning of any type can be made in one top-most layer. But its postprocessing shader must be applied to all layers at one pass if possible.

I found, that any lightning is top-most mix of color with ready picture (no connection with visual objects movement, which can be on layers). There is no difference what we apply: moon light, sun light, sunshine, evening, thunderbolt glowing, flying worms or all effects at once... All 1000 lights on scene are rendered as postprocessing at top-most layer.

And only that you would impelement in editor as developer - that two improvements: a) full scene postprocessing (all layers), and b) arrays in uniforms, to pass list of lights positions (when I have 30 flying glowworms).

I'll continue to make demos, but I think - this is final list of improvements, that need for lightning. All the rest can be done at current editor version with no performance impact.

coal

Have problems with multifunction shader realization. Please, see that shader

http://glslsandbox.com/e#36217.0

I can't implement it now...

coal
>Also I strongly need one postprocessing for all layers one time.

Сlarification! I need new checkbox on postprocessing layer tab: "apply posteffect to all layers before this".

With that option I can apply light to 1-6 layers from 6 layer, and not apply effect to 7-15 layers. This is more covinient for perspective imitation.

coal

Part 4. Straight rays. 

No editor modifications need, except layers mix for posteffect

http://seganelservice.ru/WebGL/Wade23/
http://seganelservice.ru/WebGL/Wade25/

Shader code

vec2 uv = uvAlphaTime.xy; 

vec4 realColor = texture2D(uDiffuseSampler, uv);

//rays parameters
vec3 color;
float angle=3.8; //angle of lines
float shift = -3.9; //shift of lines
float intensity = 0.3; //rays intensity
float density = 0.5; //rays density
const int raysCount = 20; //rays count
float minwidth = 0.15; //mnimal width of ray
float time = uvAlphaTime.w/5.;

float sincostime = (sin(time)+cos(time))/2.0;
float y = uv.x*angle+shift;
vec3 lightColor = vec3(1.0,1.0,1.0);
for(int i=0; i < raysCount ; i++)
{
    y += sin(float(i)+sincostime)/density;
    float linewid = abs(sin(float(i)*time/10.0)/5.0)+minwidth;
    float pct = smoothstep( y-linewid, y, uv.y) - smoothstep( y, y+linewid, uv.y);
    color += mix(color, pct*lightColor,1.1);
}
color*=intensity;

gl_FragColor = realColor+vec4(color,1.0);

 

Gio

Nice demos!

Yeah I agree that we should have a way of merging some layers before post processing is applied, in some cases. I'll have to think about the best way of achieving that, because in many other cases, you still want to have a separate post process pass on each layer.

Arrays can also probably be added as new uniform types supported by the framework, that should be doable, I'll look into it.

The multifunction shaders though are going to be difficult to implement. At the moment it's all in one single function, to remove some complexity from the process of assigning a shader to a sprite. I'm not sure how multifunction shaders would work with the current architecture, something to think about.

coal

The multifunction shaders though are going to be difficult to implement. At the moment it's all in one single function, to remove some complexity from the process of assigning a shader to a sprite. I'm not sure how multifunction shaders would work with the current architecture, something to think about.

Of course, I understand that fully. May be add some internal parsing?

Parsing, when that game shader code

float noise(float x, float y)
{
  //some code...
}

float val = noise(x,y);
//some code...

Will be internally transformed to:

float noise(float x, float y)
{
  //some code...
}

void main()
{
   float val = noise(x,y);
   //some code...
}

 

I tried to expand shader funcs at one main method, but... I can't - too much code...

It's not problem now, I can use noise texture, for example, in this case, if I want this shader... But in case of raymarching - I can't replace funcs by texture, there are special funcs, not noise only...

coal

Part 5. Mask lightning

http://seganelservice.ru/WebGL/Wade31/ - click on it, to hear audio

all magick is:

a) original picture

b) mask in Photoshop 

c) Shader, that make light by mask. It has uniform sampler2D LightPosSampler, which is picture from b)

vec2 uv = uvAlphaTime.xy;
vec4 realColor = texture2D(uDiffuseSampler, uv);
vec4 lightColor = texture2D(LightPosSampler, uv);

float longsin = abs(sin(uvAlphaTime.w*1.0)/2.0)+.85;
lightColor.r *= longsin;
//lightColor.r +=abs(sin(uvAlphaTime.w*1000.))/30.0;
lightColor.g=lightColor.r;

vec4 nightColor = realColor;
nightColor.r*=0.1;
nightColor.g*=0.1;
nightColor.b*=0.5;
    
gl_FragColor=(realColor+lightColor)*lightColor.r/3.0+nightColor;

 

This can be used to apply any effect on any part of screen

coal

Last demo modification:

Added: moonglow with mask. And thunderbolt with mask (1 sprite, 1 shader)

http://seganelservice.ru/WebGL/Wade32/

Here is mask

Here is sprite shader

vec2 uv = uvAlphaTime.xy;
vec4 realColor = texture2D(uDiffuseSampler, uv);
vec4 lightColor = texture2D(LightPosSampler, uv);

float longsin = abs(sin(uvAlphaTime.w*1.0)/2.0)+.85;
lightColor.r *= longsin;
lightColor.g=lightColor.r;

vec4 nightColor = realColor;
nightColor.r*=0.1;
nightColor.g*=0.1;
nightColor.b*=0.5;

vec4 lightColor2 = texture2D(LightPosSampler2, uv);

float thunder = sin(uvAlphaTime.w*1.0);
if (lightColor2.b>0.001)
{
    float kk;
    if (thunder<=0.9)
    {
       kk=10.;   
    }
    else
    {
        kk=5.0;
    }
    nightColor.r+=nightColor.r*lightColor2.b*kk*(sin(uvAlphaTime.w*1.0)/4.0+1.5);
    nightColor.g+=nightColor.g*lightColor2.b*kk*(sin(uvAlphaTime.w*1.0)/4.0+1.5);
}
if (thunder>0.9)
{
    float k = sin(uvAlphaTime.w*100.0)*0.5+1.5;
    if (lightColor2.g>0.0)
    {
        nightColor*=k;
    }
    else
    {
        nightColor-=nightColor*k/15.;
    }
}

    
gl_FragColor=(realColor+lightColor)*lightColor.r/3.0+nightColor;

 

coal

Topic resume: work with light is very easy. But framework needs some little work for convinience.

coal

Part 6. Border highlighting

http://seganelservice.ru/WebGL/Wade33/

This is Sobel shader, which can do such filtering in posteffect:

//common vars for all filtering
int mode = 1;
bool invert = false;
float realWeight = 1.0;
vec3 color = vec3(1.0,1.0,0.0);
vec2 vUv = uvAlphaTime.xy;
vec4 realColor = texture2D( uDiffuseSampler, vUv);


//------Sobel--------//
float sobelWeight = 1.0;

vec2 resolution = vec2(1920.,1080.);
vec2 texel = vec2( 1.0 / resolution.x, 1.0 / resolution.y );

// kernel definition (in glsl matrices are filled in column-major order)
const mat3 Gx = mat3( -1, -2, -1, 0, 0, 0, 1, 2, 1 ); // x direction kernel
const mat3 Gy = mat3( -1, 0, 1, -2, 0, 2, -1, 0, 1 ); // y direction kernel

// fetch the 3x3 neighbourhood of a fragment

// first column
float tx0y0 = texture2D( uDiffuseSampler, vUv + texel * vec2( -1, -1 ) ).r;
float tx0y1 = texture2D( uDiffuseSampler, vUv + texel * vec2( -1,  0 ) ).r;
float tx0y2 = texture2D( uDiffuseSampler, vUv + texel * vec2( -1,  1 ) ).r;

// second column
float tx1y0 = texture2D( uDiffuseSampler, vUv + texel * vec2(  0, -1 ) ).r;
float tx1y1 = texture2D( uDiffuseSampler, vUv + texel * vec2(  0,  0 ) ).r;
float tx1y2 = texture2D( uDiffuseSampler, vUv + texel * vec2(  0,  1 ) ).r;

// third column
float tx2y0 = texture2D( uDiffuseSampler, vUv + texel * vec2(  1, -1 ) ).r;
float tx2y1 = texture2D( uDiffuseSampler, vUv + texel * vec2(  1,  0 ) ).r;
float tx2y2 = texture2D( uDiffuseSampler, vUv + texel * vec2(  1,  1 ) ).r;

// gradient value in x direction
float valueGx = Gx[0][0] * tx0y0 + Gx[1][0] * tx1y0 + Gx[2][0] * tx2y0 + 
Gx[0][1] * tx0y1 + Gx[1][1] * tx1y1 + Gx[2][1] * tx2y1 + 
Gx[0][2] * tx0y2 + Gx[1][2] * tx1y2 + Gx[2][2] * tx2y2; 

// gradient value in y direction
float valueGy = Gy[0][0] * tx0y0 + Gy[1][0] * tx1y0 + Gy[2][0] * tx2y0 + 
Gy[0][1] * tx0y1 + Gy[1][1] * tx1y1 + Gy[2][1] * tx2y1 + 
Gy[0][2] * tx0y2 + Gy[1][2] * tx1y2 + Gy[2][2] * tx2y2; 

// magnitute of the total gradient
float G = sqrt( ( valueGx * valueGx ) + ( valueGy * valueGy ) );

//------Sobel--------//


//------Last common mix--------//
if (invert)
{
    G=1.-G;
}

vec4 filteredFinal = G *sobelWeight * vec4(color,1.);
vec4 realFinal = realColor*realWeight;
if (mode==0)
{
    //real color
    gl_FragColor = realFinal;
}
if (mode==1)
{
    //filtered color
    gl_FragColor = filteredFinal;
}
else if (mode==2)
{
    //max of colors
    gl_FragColor = max(realFinal, filteredFinal);
}
else if (mode==3)
{
    //min of colors
    gl_FragColor = min(realFinal, filteredFinal);
}
else if (mode==4)
{
    //mul colors
    gl_FragColor = realFinal*filteredFinal;
}
else if (mode==5)
{
    //div colors
    gl_FragColor = realFinal/filteredFinal;
}
else if (mode==6)
{
    //add colors
    gl_FragColor = realFinal+filteredFinal;
}
else if (mode==7)
{
    //sub colors
    gl_FragColor = realFinal-filteredFinal;
}
//------Last common mix--------//

 

 

Post a reply
Add Attachment
Submit Reply
Login to Reply