Bind custom uniform values to a 2D filter (using bgl wrapper)

This script allows to add custom uniforms like textures, vectors and matrices to a custom 2D filter.


2D Filter.zip (104 KB)

The example shows how to

  • bind a texture to 2D-Filter
  • bind a float vector4 array to 2D-Filter
  • bind a matrix4 to 2D-Filter
  • bind a float uniform to 2D-Filter (useless. Only for testing purpose)

The custom 2D-Filter “Font” is used for testing to print out the uniforms.

Warning:
There is a memory leak. Because every time if Blender is started the texture counter will increased.
Freeing the textures with OpenGL is not working. Actually I don’t have an working solution for this problem.

Here is a better way to load textures into a 2D filter (need Blender 2.77 or up).
This version don’t load the texture from the disk, instead it use a Blender (material) texture.
customTex2Dfilter.blend (89.1 KB)

It seems there is a mistake in line 44, because the console show me:
File “filterBindUniform”, line 44, in <module>
TypeError: must be bgl.Buffer, not None

Or it’s because I use 2.74?

The texture was missing.

Can you provide some examples of use cases (not files just the words)

I think from what I understand, you could add a filter over the screen that is a hud background?

Ah this sounds very useful. I’ll see what I can do with it…

EDIT:
It’s running at about 5 frames per second.
I’m using blender 2.75…

GPU latency and logic seem to be the main culprits.
No errors in console. The image apears, all text is drawn correctly, but so, so, so slow.

Can you provide some examples of use cases (not files just the words)

I think from what I understand, you could add a filter over the screen that is a hud background?

No it is not a hud display. The shader is only for testing to check if the uniforms are bound correctly.

Actually for 2D-Filters only can bind a float properties to an uniform (Last week I made a patch which allows integer too).
The example should show how to bind other uniform types like textures, matrices, vectors.
Vectors are useful for position data like sun position eg. for a light scattering shader.
Matrices are useful if you want to transfer the camera projection matrix eg. for a deferred shader.
Textures can be used to bind noise (perlin) textures eg. for a cloud shader. So you don’t need to calculate a noise function in the shader which costs allot of performance.

It’s running at about 5 frames per second.

Yes I know the shader is very slow. I don’t optimized it because it was only for testing purpose.
If you reduce the amount of dynamic text (variables) the shader should be faster.
When I have time I can try to optimize it. If you have an use for it.

That’s a great ressource! Thanks!

Can someone tell me how to load more than one texture to the shader?

I have tried this but it’s not right because it load only the second texture or it’s overwrite the first one I don’t know.

#HG1 24.06.2015
#Using OpenGL Wrapper (bgl) to add uniforms to a 2D Filter

from bge import logic, texture
from bgl import * #Iam lazy I don't want to type always bgl.gl....
 
cont = logic.getCurrentController()
own = cont.owner
texList =["Cloth.png", "Cloth1.png"]

if own["program"] == -1:
    #Find last shader
    #Actually there is no good way to find always the right 2D filter 
    #in the actuators stack. So move the to the filter that is attached    
    #to the "filterBindUniform" script to the last position.
    for prog in range(32767):
        if glIsProgram(prog) == True:
            program = prog
    
    
    #Get the shader number   
    maxCount = 1
    count = Buffer(GL_INT, 1)
    shaders = Buffer(GL_BYTE, [maxCount])
    glGetAttachedShaders(program, maxCount, count, shaders)

    #Print the shader source     
    #maxLength = 64000
    #length = Buffer(GL_INT, 1)
    #source = Buffer(GL_BYTE, [maxLength])
    #glGetShaderSource(shaders[0], maxLength, length, source)
    #print("".join(chr(source[i]) for i in range(length[0])))

    #Load texture from disk and store it into an image buffer
    for tex in texList:
        imagePath = logic.expandPath('//' + tex)
        image = texture.ImageFFmpeg(imagePath)
        image_buffer_1 = image.image

        #Generate new texture
        id_buf = Buffer(GL_INT, 1)
        glGenTextures(1, id_buf)

        #Steup and bind texture
        glBindTexture(GL_TEXTURE_2D, id_buf[0])
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.size[0], image.size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, image_buffer_1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        #glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        #glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        own[tex] = id_buf[0]
        own["program"] = program


program = own["program"]
texture = own[texList[0]]
texture2 = own[texList[1]]

glActiveTexture(GL_TEXTURE3)
glBindTexture(GL_TEXTURE_2D, texture)
glBindTexture(GL_TEXTURE_2D, texture2)

#Use program necessary to update uniforms
glUseProgram(program)

# Bind uniform's texture and time1 to shader 
glUniform1i(glGetUniformLocation(program, "image"), 3)
glUniform1i(glGetUniformLocation(program, "image2"), 3)

Edit:
Okay I have managed it… I have forgot to change the active texture:

...glActiveTexture(GL_TEXTURE4)
glBindTexture(GL_TEXTURE_2D, texture2)
glUniform1i(glGetUniformLocation(program, "image2"), 4)...

texture = own[texList[0]]
texture2 = own[texList[1]]

glActiveTexture(GL_TEXTURE3)
glBindTexture(GL_TEXTURE_2D, texture)
glActiveTexture(GL_TEXTURE4)
glBindTexture(GL_TEXTURE_2D, texture2)

Strange that this works. Because in your code example the texList owns the names of the image (Cloth.png, Cloth1.png) and not the buffer ID (id_buff) generated with glGenTextures.


#HG1 24.06.2015
#Using OpenGL Wrapper (bgl) to add uniforms to a 2D Filter

from bge import logic, texture
from bgl import * #I am lazy I don't want to type always bgl.gl....
 
cont = logic.getCurrentController()
own = cont.owner
texList =["Cloth.png", "Cloth1.png"]

if own["program"] == -1:
    #Find last shader
    #Actually there is no good way to find always the right 2D filter 
    #in the actuators stack. So move the to the filter that is attached    
    #to the "filterBindUniform" script to the last position.
    for prog in range(32767):
        if glIsProgram(prog) == True:
            program = prog
    
    
    #Get the shader number   
    maxCount = 1
    count = Buffer(GL_INT, 1)
    shaders = Buffer(GL_BYTE, [maxCount])
    glGetAttachedShaders(program, maxCount, count, shaders)

    #Print the shader source     
    #maxLength = 64000
    #length = Buffer(GL_INT, 1)
    #source = Buffer(GL_BYTE, [maxLength])
    #glGetShaderSource(shaders[0], maxLength, length, source)
    #print("".join(chr(source[i]) for i in range(length[0])))

    own["idList"] = []

    #Load texture from disk and store it into an image buffer
    for tex in texList:
        imagePath = logic.expandPath('//' + tex)
        image = texture.ImageFFmpeg(imagePath)
        image_buffer_1 = image.image

        #Generate new texture
        id_buf = Buffer(GL_INT, 1)
        glGenTextures(1, id_buf)

        #Steup and bind texture
        glBindTexture(GL_TEXTURE_2D, id_buf[0])
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.size[0], image.size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, image_buffer_1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        #glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        #glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        own["idList"].append(id_buf[0])
        own["program"] = program


program = own["program"]
texture = own["idList"][0]
texture2 = own["idList"][1]

glActiveTexture(GL_TEXTURE3)
glBindTexture(GL_TEXTURE_2D, texture)
glActiveTexture(GL_TEXTURE4)
glBindTexture(GL_TEXTURE_2D, texture2)

#Use program necessary to update uniforms
glUseProgram(program)

# Bind uniform's texture and time1 to shader 
glUniform1i(glGetUniformLocation(program, "image"), 3)
glUniform1i(glGetUniformLocation(program, "image2"), 4)

Yes, later I have seen that this is wrong and I changed my code but I have forgot to edit my post. Thanks for your correction.

Before I forgot to mention. In my examples there is a little fault. Because to make the code a little bit clearer I don’t used glDeleteTextures to to free the texture if the engine is stopped.
If you want to do it correct, then you can check if the ESC key is pressed or use a class destructor to delete the texture properly.

Hi, if you’ve the time, could you take a look at this: https://drive.google.com/file/d/0B3GouQIyoCmrWEJvWWMzRVFzVTQ/view?usp=sharing and tell me what I did wrong?
(I tried to implement this shader: http://www.geeks3d.com/20101228/shader-library-frosted-glass-post-processing-shader-glsl/ as a 2D Filter but I can’t bind my noise texture…) Thanks!

EDIT: It’s ok… I added a plane with a material and empty texture slot… Very weird. I don’t understand but it works. http://www.mediafire.com/download/szi7q94g7mg8lzy/frost.rar

Finally I replaced noise texture with pseudo random code: http://www.mediafire.com/download/7i2dmfws181et7s/frost.blend

Youle, in the first example you have enabled the true pulse on the always sensor which is triggering the 2D filter actuator. This will cause the problem.

Using a texture should be faster then calculating one.

Ohhh. Ok Thanks very much HG1 :slight_smile:

How come this doesn’t work in standalone player?

If I go to Scene > Standalone Player > Start it just shows the regular plane. I’m on Blender 2.78, and yes, I tried delaying the getTextureBindcode.

@FarkR: This is a matter of delay: http://pasteall.org/blend/index.php?id=44743. When you start game engine in embedded, the data needed to get the right infos in the python script is already available because the data has already been created by the viewport code. When you launch in standalone, some data is not available at the first frame so you have to wait 1 frame to make it available.

Sorry my bad. I excellently connected the delay and always sensor wrong in my example.
I fixed the example. Thanks for reporting it.

@HG1

I’ve used your code as reference to implement a similar system on my addon. Thanks. However it doesn’t free the textures loaded from disk, just like yours, have you found how to do this recently?

Before I forgot to mention. In my examples there is a little fault. Because to make the code a little bit clearer I don’t used glDeleteTextures to to free the texture if the engine is stopped.
If you want to do it correct, then you can check if the ESC key is pressed or use a class destructor to delete the texture properly.

is it this?

@elmeunick9: you’re talking about your upbge addon? Because we have a different system to bind uniforms in 2D filters. All a python API. Connect to #upbgecoders if you need help.

Basically a quick upbge snippet from what I remember:


import bge

cont = bge.logic.getCurrentController()
own = cont.owner

scene = bge.logic.getCurrentScene()

filter = scene.filterManager.getFilter(0)

filter.setTexture(0, bindcode, "tex") #0 is an empty slot. There are 8 slots from 0 to 7 where you can bind the texture you want. bindcode is accessed like that: objects.meshes[0].materials[0].textures[0].bindCode. "tex" is the texture name in the 2D filter.

filter.setUniform1f("time", own["timer"])
filter.setUniformMatrixetc... same as in BL_Shader


EDIT: Ah sorry I didn’t read correctly, this was about freeing texture loaded from disk… (we use an object with textures so they are already treated by the gpu in upbge).