GLSL Electricity Shader

Hey. So, RenderMonkey is a GLSL shader tool available for Windows, and it came with an electricity shader. I adapted / ported it to work with the BGE.

It requires Blender 2.5+, I believe, to work. You can download it here.

says
"unable to initialize image(0) in MAMaterial, image will not be available

Blender Game Engine Started
Invalid uniform value: time.
Invalid uniform value: noisesampler.
Blender Game Engine Finished"
in both 2.59 and 2.6

and this is what the plane looks like.

Sorry about that - I forgot that the shader relies on a noise texture and used one, but didn’t pack it into the blend file. It’s fixed now.

Thank you it works now. and is really cool, I am going to see if I can make some lightning based skills with it tomorrow.

thats really cool!!
i created an editor to tweak it a little in relatime.
There might be even other things like lava or so possible with this shader—:rolleyes:

Attachments

ElectricityShader_editor.blend (522 KB)

It is only a quick code change. You will get an error message but it will work.


import bge
from bge import logic
VertexShader = """
varying vec2 vtexcoord;
void main()
{
    vec2 position;
    position.xy = sign(gl_Vertex.xy);
    vtexcoord = position.xy;
    gl_Position = ftransform();
    //gl_Position = vec4(position.xy, 0.0, 1.0);    // Screen aligned, if you want that
}
"""
FragmentShader = """
uniform float time;
uniform float speed;
uniform float noise;
uniform float sampledist;
uniform float height;
uniform float glowfalloff;
uniform float glowstrength;
uniform float ambientglow;
uniform float ambientglowheightscale;
uniform sampler2D noisesampler;
varying vec2 vtexcoord;
void main()
{
    //float speed = 0.25;
    //float noise = 0.78;        // These variables should be replaced with uniform variables
    //float sampledist = 0.0076; // that can be edited from the object's properties
    //float height = 0.44;
    //float glowfalloff = 0.024;
    //float glowstrength = 144.0;
    //float ambientglow = 0.5;
    //float ambientglowheightscale = 1.68;
    
    vec4 color = vec4(0.965, 0.624, 1.0, 1.0);
    vec2 t = vec2(speed * time * .5871 - noise * abs(vtexcoord.y), speed * time);
    float xs0 = vtexcoord.x - sampledist;
    float xs1 = vtexcoord.x;
    float xs2 = vtexcoord.x + sampledist;
    float noise0 = texture2D(noisesampler, vec2(xs0 , t.y + (t.x * .3)) ).x;    // Change t.y + (t.x * .3) to t.y - etc. to see a sucking electricity
    float noise1 = texture2D(noisesampler, vec2(xs1 , t.y + (t.x * .3)) ).x;
    float noise2 = texture2D(noisesampler, vec2(xs2 , t.y + (t.x * .3)) ).x;
    float mid0 = height * (noise0 * 2.0 - 1.0) * (1.0 - xs0 * xs0);
    float mid1 = height * (noise1 * 2.0 - 1.0) * (1.0 - xs1 * xs1);
    float mid2 = height * (noise2 * 2.0 - 1.0) * (1.0 - xs2 * xs2);
    float dist0 = abs(vtexcoord.y - mid0);
    float dist1 = abs(vtexcoord.y - mid1);
    float dist2 = abs(vtexcoord.y - mid2);
    
    float glow = 1.0 - pow(0.25 * (dist0 + 2.0 * dist1 + dist2), glowfalloff);
    
    float amb = ambientglow * (1.0 - xs1 * xs1) * (1.0 - abs(ambientglowheightscale * vtexcoord.y) );
    //float amb = 0.0;
    
 vec4 final = (glowstrength * glow * glow + amb) * color;
    //final *= .75;
    gl_FragColor = final;
}
"""
def Electricity():
    
    cont = logic.getCurrentController()
    obj = cont.owner
    
    if not 'time' in obj:
        obj['time'] = 0.0
    obj['time'] += 1.0 / logic.getLogicTicRate()
    meshIndex = 0
    mesh = obj.meshes[0]
      
    mat = mesh.materials[0]
    shader = mat.getShader()
    if shader != None:
        shader.setUniform1f('time', obj['time'])
        shader.setUniform1f('speed',obj['speed'])
        shader.setUniform1f('noise',obj['noise'])
        shader.setUniform1f('sampledist',obj['sampledist'])
        shader.setUniform1f('height',obj['height'])
        shader.setUniform1f('glowfalloff',obj['glowfalloff'])
        shader.setUniform1f('glowstrength',obj['glowstrength'])
        shader.setUniform1f('ambientglow',obj['ambientglow'])
        shader.setUniform1f('ambientglowheightscale',obj['ambientglowheightscale'])
        shader.setSampler('noisesampler', 0)
        shader.setSource(VertexShader,FragmentShader,1)

all right! Thats exactly what i did, but i stopped because of the error message…
Then lets tweak!

I found the fault.
The shader must be loaded before you assign the values.
And there was no check if the shader is not valid “if not shader.isValid():”


import bge
from bge import logic
VertexShader = """
varying vec2 vtexcoord;
void main()
{
    vec2 position;
    position.xy = sign(gl_Vertex.xy);
    vtexcoord = position.xy;
    gl_Position = ftransform();
    //gl_Position = vec4(position.xy, 0.0, 1.0);    // Screen aligned, if you want that
}
"""
FragmentShader = """
uniform float time;
uniform float speed;
uniform float noise;
uniform float sampledist;
uniform float height;
uniform float glowfalloff;
uniform float glowstrength;
uniform float ambientglow;
uniform float ambientglowheightscale;
uniform sampler2D noisesampler;
varying vec2 vtexcoord;
void main()
{
    //float speed = 0.25;
    //float noise = 0.78;        // These variables should be replaced with uniform variables
    //float sampledist = 0.0076; // that can be edited from the object's properties
    //float height = 0.44;
    //float glowfalloff = 0.024;
    //float glowstrength = 144.0;
    //float ambientglow = 0.5;
    //float ambientglowheightscale = 1.68;
 
    vec4 color = vec4(0.965, 0.624, 1.0, 1.0);
    vec2 t = vec2(speed * time * .5871 - noise * abs(vtexcoord.y), speed * time);
    float xs0 = vtexcoord.x - sampledist;
    float xs1 = vtexcoord.x;
    float xs2 = vtexcoord.x + sampledist;
    float noise0 = texture2D(noisesampler, vec2(xs0 , t.y + (t.x * .3)) ).x;    // Change t.y + (t.x * .3) to t.y - etc. to see a sucking electricity
    float noise1 = texture2D(noisesampler, vec2(xs1 , t.y + (t.x * .3)) ).x;
    float noise2 = texture2D(noisesampler, vec2(xs2 , t.y + (t.x * .3)) ).x;
    float mid0 = height * (noise0 * 2.0 - 1.0) * (1.0 - xs0 * xs0);
    float mid1 = height * (noise1 * 2.0 - 1.0) * (1.0 - xs1 * xs1);
    float mid2 = height * (noise2 * 2.0 - 1.0) * (1.0 - xs2 * xs2);
    float dist0 = abs(vtexcoord.y - mid0);
    float dist1 = abs(vtexcoord.y - mid1);
    float dist2 = abs(vtexcoord.y - mid2);
 
    float glow = 1.0 - pow(0.25 * (dist0 + 2.0 * dist1 + dist2), glowfalloff);
 
    float amb = ambientglow * (1.0 - xs1 * xs1) * (1.0 - abs(ambientglowheightscale * vtexcoord.y) );
    //float amb = 0.0;
 
 vec4 final = (glowstrength * glow * glow + amb) * color;
    //final *= .75;
    gl_FragColor = final;
}
"""
def Electricity():
 
    cont = logic.getCurrentController()
    obj = cont.owner
 
    #if not 'time' in obj:
    #    obj['time'] = 0.0
    #obj['time'] += 1.0 / logic.getLogicTicRate()
    meshIndex = 0
    mesh = obj.meshes[0]
 
    mat = mesh.materials[0]
    shader = mat.getShader()
    if shader != None:
        if not shader.isValid():
            shader.setSource(VertexShader,FragmentShader,1)
        shader.setUniform1f('time', obj['time'])
        shader.setUniform1f('speed',obj['speed'])
        shader.setUniform1f('noise',obj['noise'])
        shader.setUniform1f('sampledist',obj['sampledist'])
        shader.setUniform1f('height',obj['height'])
        shader.setUniform1f('glowfalloff',obj['glowfalloff'])
        shader.setUniform1f('glowstrength',obj['glowstrength'])
        shader.setUniform1f('ambientglow',obj['ambientglow'])
        shader.setUniform1f('ambientglowheightscale',obj['ambientglowheightscale'])
        shader.setSampler('noisesampler', 0)

There was still an error, you wrote “Time” instead of “time”, anyway upon it is working now with no errors, cool!

Just beautiful!
Thank you for sharing!

how did you guys make it go alpha ish…

Never mind wooooo!!!

Fantastic shader! I love playing with it. I wonder how to apply it on a more complex mesh. If i subdivide the plane then the shader does’nt look good anymore. By the way thanks a lot it helped me to learn some important things.

hi! is it possible to replace this shader in cycles glsl realtime in viewport??

Not directly. But maybe it is possible to rewrite it to an OSL shader. Then you can use it with the Script-Node.

Very nice! Thanks for sharing!

hi RZ3X, i’ve just port the code to glsl node and it seems to work, but there is a problem with alpha transparency in glsl cycles viewport.(maybe it works in the viewport pbr branch but i haven’t test)


The blendfile :ElectricityShaderGlslNodeversion.blend (680 KB)

Attachments