GLSL shaders in Panda

drwr’s new draw callback feature opened up a world of opportunities.
I couldn’t resist playing with it so I ended up applying GLSL shaders to Panda objects. To my big surprise, it worked. :slight_smile:

Here’s some very simple code that applies a GLSL shader to the Panda model, using pyOpenGL.
The shader just shows the texture that’s applied, but inverted. Not much special, but it at least shows that texture and coords work correctly.

from pandac.PandaModules import PythonCallbackObject
from pandac.PandaModules import CallbackNode, VBase3
from direct.directbase import DirectStart

from OpenGL.GL import *
import sys

VSHADER = """
void main() {
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
"""
FSHADER = """
uniform sampler2D tex;
void main() {
  gl_FragColor.rgb = 1.0 - texture2D(tex,gl_TexCoord[0].st).rgb;
}
"""

def init(cbdata):
  """We'll be compiling & assigning the shader here.
  This method will only be called once."""
  v = glCreateShader(GL_VERTEX_SHADER)
  f = glCreateShader(GL_FRAGMENT_SHADER)
  glShaderSource(v, VSHADER)
  glShaderSource(f, FSHADER)
  glCompileShader(v)
  glCompileShader(f)
  program = glCreateProgram()
  glAttachShader(program,v)
  glAttachShader(program,f)
  glLinkProgram(program)
  glUseProgram(program)
  glDeleteShader(v)
  glDeleteShader(f)
  # We don't need to set the shader again. Clear the callback.
  cbnode.clearDrawCallback()

# Make sure we're using da OpenGL.
if base.pipe.getInterfaceName() != "OpenGL":
  print "This program requires OpenGL."
  sys.exit(1)

# Set up the callback object
cbnode = CallbackNode("cbnode")
cbnode.setDrawCallback(PythonCallbackObject(init))
cbnp = render.attachNewNode(cbnode)

# Load the panda and reparent it to the callback object.
panda = loader.loadModel("panda")
panda.reparentTo(cbnp)

# Let it rotate to show that transforms work well too.
panda.hprInterval(2.0, VBase3(360, 0, 0)).loop()

# Put the camera where it will be able to actually see something.
base.trackball.node().setPos(0, 30, -7)

run()

The code will only run with latest CVS version of panda (or wait till 1.6.0).

Thanks so much drwr, for adding the callback feature to Panda.

PS. This made me realize how easy it would be if such a system would be implemented into Panda. Maybe one day panda will support GLSL without the need for this hacky pyOpenGL callback :smiley:

Do you think we could get glsl shader as an alternative to Cg in a real way? On the other had Cg is better then glsl.

Yeah, but GLSL seems to be more commonly used, and is IMHO slightly easier to use and implement than Cg.
A lot of users don’t care if they are not compatible with DirectX, so I don’t see a reason not to implement it.

But I agree, I’d still stick to Cg myself.

I was interested in trying this, since OS X has a tool called OpenGL Shader Builder which deals with GLGS shaders in real time, so I can explore them a bit more easily. (If you can suggest a CG shader tool for the mac that does the same I would gladly take that).

Anyways, when I run this on my mac I get the following error.

Traceback (most recent call last):
  File "test2.py", line 27, in init
    glCompileShader(v)
  File "/Library/Python/2.5/site-packages/OpenGL/GL/VERSION/GL_2_0.py", line 96, in GLSLCheckError
    getter( cArguments[0], key, ctypes.byref(status))
  File "/Library/Python/2.5/site-packages/OpenGL/lazywrapper.py", line 9, in __call__
    return wrapper( baseFunction, *args, **named )
TypeError: glGetShaderiv() takes exactly 3 arguments (4 given)
:util(error): Exception occurred in PythonCallbackObject
Traceback (most recent call last):
  File "Panda3D-tpl-rw/Panda3D/1.6.1/lib/direct/showbase/ShowBase.py", line 1568, in __igLoop
  File "test2.py", line 27, in init
    glCompileShader(v)
  File "/Library/Python/2.5/site-packages/OpenGL/GL/VERSION/GL_2_0.py", line 96, in GLSLCheckError
    getter( cArguments[0], key, ctypes.byref(status))
  File "/Library/Python/2.5/site-packages/OpenGL/lazywrapper.py", line 9, in __call__
    return wrapper( baseFunction, *args, **named )
TypeError: glGetShaderiv() takes exactly 3 arguments (4 given)
:task(error): Exception occurred in PythonTask igLoop
Traceback (most recent call last):
  File "test2.py", line 59, in <module>
    run()
  File "Panda3D-tpl-rw/Panda3D/1.6.1/lib/direct/showbase/ShowBase.py", line 2423, in run
  File "Panda3D-tpl-rw/Panda3D/1.6.1/lib/direct/task/TaskNew.py", line 471, in run
  File "Panda3D-tpl-rw/Panda3D/1.6.1/lib/direct/task/TaskNew.py", line 429, in step
  File "Panda3D-tpl-rw/Panda3D/1.6.1/lib/direct/showbase/ShowBase.py", line 1568, in __igLoop
  File "test2.py", line 27, in init
    glCompileShader(v)
  File "/Library/Python/2.5/site-packages/OpenGL/GL/VERSION/GL_2_0.py", line 96, in GLSLCheckError
    getter( cArguments[0], key, ctypes.byref(status))
  File "/Library/Python/2.5/site-packages/OpenGL/lazywrapper.py", line 9, in __call__
    return wrapper( baseFunction, *args, **named )
TypeError: glGetShaderiv() takes exactly 3 arguments (4 given)

Any thoughts?

Hmm. Looks like a bug inside pyOpenGL. What version do you have? Could you try upgrading?

I’m using PyOpenGL3, because I can’t figure out how to get PyOpenGL 2 to install properly. I’ve reinstalled a couple of times, along with some other voodoo that I tried and still no luck.

I’ll try contacting the PyOpenGL people about this and see if I’m alone.

I did not know about this. Still not as simple as FX Composer on Windows, but thanks for mentioning this tool!

I’ve just checked in native support for GLSL shaders into Panda CVS. It’s still a bit experimental, but functional.

It’s real GLSL (no parameter name limits) with the exception that you need to have “//GLSL” at the top of the files, and to allow all shaders in one file, replace “main” with “vshader” or “fshader” or “gshader”.
The syntax is like this:

//GLSL

void vshader() {
  gl_Position = ftransform();
}

uniform sampler2D tex;
void fshader() {
  gl_FragColor = texture2D(tex, gl_TexCoord[0]);
}

Would this be compatible with Panda running DirectX 9?

I’m not sure, but I don’t think so since GLSL is the OpenGL Shading Language.

Nope, it isn’t. For DX9, stick to Cg.

Someone DirectX fan can add HLSL support Panda, if it’s really needed.

Did anyone manage to run pro-soft’s code on a Mac?
I get the exact same error as emeryc.

I also tried using the second example with GLSL and got the following error:

:gobj(error): GLSL shaders must have separate shader bodies!
:gobj(error): Shader encountered an error.
:display:gsg:glgsg(error): An error occurred while compiling shader!
ERROR: 0:9: 'gl_FragColor' : undeclared identifier 
ERROR: 0:9: 'texture2D' : no matching overloaded function found 
:display:gsg:glgsg(error): at 392 of panda/src/glstuff/glShaderContext_src.cxx : GL error 1281

Sorry, I’ve changed the interface. The shaders should now be separate, like any normal GLSL shader. You’ll end up with two glsl shader files that have an entry point called “main”, as usual.

Here’s how you should then load it:

shader = Shader.load(Shader.SLGLSL, "vertexprog.glsl", "fragmentprog.glsl", "geometryprog.glsl")

main.py:

from pandac.PandaModules import loadPrcFileData
loadPrcFileData('', 'basic-shaders-only 0')
loadPrcFileData('', 'sync-video 0')
loadPrcFileData('', 'show-frame-rate-meter 1')
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
from direct.interval.IntervalGlobal import Sequence
import direct.directbase.DirectStart
from pandac.PandaModules import Point3, Vec4, PTA_LVecBase4f, Shader, UnalignedLVecBase4f

class World(DirectObject):
        def __init__(self):
            self.accept("escape", __import__("sys").exit, [0])

            self.model = Actor('panda-model', {'walk': 'panda-walk4'})
            self.model.setScale(0.005)
            self.model.flattenLight()
            self.model.setPos(-2.7,300,-5)
            self.model.loop('walk')
            self.model.reparentTo(render)
            interval = self.model.posInterval(20, Point3(-2.7, 200, -5), startPos=Point3(-2.7, 300, -5))
            sequence = Sequence(interval)
            sequence.loop()

            k = 256
            self.model.setShader(Shader.load(Shader.SLGLSL, 'vinstance.glsl', 'finstance.glsl'))
            #self.model.setShader(Shader.load(Shader.SLCg, 'vinstance.cg', 'finstance.cg'))


w = World()
run()

vinstance.glsl:

//GLSL

void main() {
  gl_Position = ftransform();
}

finstance.glsl:

//GLSL

//uniform sampler2D tex;
void main() {
  //gl_FragColor = texture2D(tex, gl_TexCoord[0]);
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

I got the following error:

:display:glxdisplay(warning): No suitable FBConfig contexts available; using XVisual only.
depth_bits=24 color_bits=24 alpha_bits=8 stencil_bits=8 back_buffers=1 force_hardware=1 
:gobj(error): GLSL shaders must have separate shader bodies!
:gobj(error): Shader encountered an error.

Which version of Panda3D are you using?

Panda3d’s versiov is “panda3d1.9 1.9.0~cvs20130507~quantal24”. Drive is Bumblebee-nvidia.

By the way, When I use:

#self.model.setShader(Shader.load(Shader.SLGLSL, 'vinstance.glsl', 'finstance.glsl'))
self.model.setShader(Shader.load(Shader.SLCg, 'vinstance.cg', 'finstance.cg'))

finstance.cg:

//Cg
//Cg profile gp4vp gp4fp

void main(float2 l_texcoord0: TEXCOORD0,
                 uniform sampler2D tex_0: TEXUNIT0,
                 out float4 o_color: COLOR)
{
      o_color = tex2D(tex_0, l_texcoord0);
}

vinstance.cg:

//Cg
//Cg profile gp4vp gp4fp

void main(float4 vtx_position: POSITION,
                 float2 vtx_texcoord0: TEXCOORD0,
                 uniform float4x4 mat_modelproj,
                 out float4 l_position : POSITION,
                 out float2 l_texcoord0 : TEXCOORD0)
{
      l_position = mul(mat_modelproj, vtx_position);
      l_texcoord0 = vtx_texcoord0;
}

It say:

:display(warning): FrameBufferProperties available less than requested.
:gobj(error): : (0) : error C0000: syntax error, unexpected $end at token "<EOF>"
:gobj(error): Shader encountered an error.
:gobj(error): : (0) : error C0000: syntax error, unexpected $end at token "<EOF>"
[VGL] NOTICE: Pixel format of 2D X server does not match pixel format of
[VGL]    Pbuffer.  Disabling PBO readback.

I don’t care use Cg or GLSL, I just want use geomerty shader.

You’re using a devel version; I remember GLSL being broken at some point on the trunk. Can you update to the latest devel version to see if it still occurs there?

Thank you!

I download panda3d1.9_1.9.0~cvs20130704~quantal49_amd64.deb, then the GLSL is work, but Cg still don’t work.

I just written my first geometry shader.

The subroutines in the Cg shader needs to be called “fshader” or “vshader” instead of “main”.

Thank you! Although I am able to use Cg shader, but Cg tutorials are too little. I could only use GLSL.