Shader implement problem

I’m trying to make a shader and implement it in Panda3D. But it has some problems.

The error msg:

Assertion failed: Shader input u_time is not present.
 at line 397 of panda/src/pgraph/shaderAttrib.cxx

Here is the main.py.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import loadPrcFileData
from panda3d.core import Shader

configVars = """
win-size 1280 720
show-frame-rate-meter 1

gl-version 3 2
textures-power-2 none
"""

loadPrcFileData("", configVars)


class MyGame(ShowBase):
    def __init__(self):
        super().__init__()
        self.set_background_color(0, 0, 0, 1)
        # self.wireframeOn()
        self.cam.setPos(0, -4, 0)

        my_shader = Shader.load(Shader.SL_GLSL,
                                vertex="shaders/combined-vert.glsl",
                                fragment="shaders/combined-frag.glsl")

        # self.plane = self.loader.loadModel("my-models/plane")
        self.jack = self.loader.loadModel("jack")

        self.jack.reparentTo(self.render)
        self.jack.set_shader_input("resolution", (self.win.getXSize(), self.win.getYSize()))
        self.jack.setShader(my_shader)

        self.accept("aspectRatioChanged", self.win_resize)

    def win_resize(self):
        # print("resize")
        self.plane.set_shader_input("resolution", (self.win.getXSize(), self.win.getYSize()))


game = MyGame()
game.run()

combined-vert.glsl

#version 330

// Vertex inputs
in vec4 p3d_Vertex;

// Uniform inputs
uniform mat4 p3d_ModelViewProjectionMatrix;

void main() {
    gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
}

combined-frag.glsl

#version 330

// window resolution
uniform vec2 resolution;

// fragment-pixel output color
out vec4 outColor;

// vignette constants
const float outerRadius = 0.65;
const float innerRadius = 0.3;
const float vignette_intensity = 0.6;

// mix intensity-weight
const float mix_intensity = 0.55;

// test time
uniform float u_time;

void main() {
//    // vertical gradient
//    vec2 pos = gl_FragCoord.xy / resolution;
//
//    // vignette
//    vec4 color = vec4(1.0);
//    vec2 relativePosition = gl_FragCoord.xy / resolution - 0.5;
//    float len = length(relativePosition);
//    float vignette = smoothstep(outerRadius, innerRadius, len);
//    color.rgb = mix(color.rgb, color.rgb * vignette, vignette_intensity);
//
//    // combining-mixing the gradient with vignette
//    color.rgb = mix(vec3(0.3, 0.5, pos.y), color.rgb, mix_intensity);
//    outColor = color;

    // test color
    vec2 coord = 6.0 * gl_FragCoord.xy / resolution;

    for (int n = 1; n < 8; n++){
        float i = float(n);
        coord += vec2(0.7 / i * sin(i * coord.y + u_time + 0.3 * i) + 0.8, 0.4 / i * sin(coord.x + u_time + 0.3 * i) + 1.6);
    }

    coord *= vec2(0.7 / sin(coord.y + u_time + 0.3) + 0.8, 0.4 / sin(coord.x + u_time + 0.3) + 1.6);

    vec3 color = vec3(0.5 * sin(coord.x) + 0.5, 0.5 * sin(coord.y) + 0.5, sin(coord.x + coord.y));

//    gl_FragColor = vec4(color, 1.0);
    outColor = vec4(color, 1.0);
}

Reference code:shadertutorialseries/014_waterColor.frag at master · lewislepton/shadertutorialseries · GitHub
video:shader tutorial series - episode 014 - water color - YouTube

I expect to use this color in the 3d model. But I don’t understand why I can’t use u_time in the Panda3D and how to replace u_time.
Screenshot 2022-03-18 at 00.55.18 AM

Thank you for read this post.

There is a shader input defined in the shader, but it needs to be specified in Panda3D. You cannot run a shader with an undefined shader input. You will have to write code in Panda3D to use setShaderInput to specify a value for this input.

Alternatively, you can replace u_time in the shader with osg_FrameTime, which Panda will be able to fill automatically with the current time in seconds.

1 Like

Thank you for your reply.
Can I say if I have any commend not sure work on Panda3D, I can try to find the commend on List of GLSL Shader Inputs — Panda3D Manual ?

I have another question about this shader. I try to make it become the camera shader. However, I don’t what I miss.

Here is the current work, I use the shader on the 3D model.
main.py:

from direct.showbase.ShowBase import ShowBase
from panda3d.core import loadPrcFileData
from panda3d.core import Shader

configVars = """
win-size 1280 720
show-frame-rate-meter 1

gl-version 3 2
textures-power-2 none
"""

loadPrcFileData("", configVars)


class MyGame(ShowBase):
    def __init__(self):
        super().__init__()
        self.set_background_color(0, 0, 0, 1)
        # self.wireframeOn()
        self.cam.setPos(0, -4, 0)

        my_shader = Shader.load(Shader.SL_GLSL,
                                vertex="shaders/waterColor-v.glsl",
                                fragment="shaders/waterColor-f.glsl")

        # self.plane = self.loader.loadModel("my-models/plane")
        self.jack = self.loader.loadModel("jack")

        self.jack.reparentTo(self.render)
        self.jack.set_shader_input("resolution", (1920, 1080))

        self.jack.setShader(my_shader)


game = MyGame()
game.run()

waterColor-v.glsl:

#version 330

// Vertex inputs
in vec4 p3d_Vertex;

// Uniform inputs
uniform mat4 p3d_ModelViewProjectionMatrix;

void main() {
    gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
}

waterColor-f.glsl

#version 330

// window resolution
uniform vec2 resolution;

// fragment-pixel output color
out vec4 outColor;


// test time

uniform float osg_FrameTime;

void main() {
    vec2 coord = 6.0 * gl_FragCoord.xy / resolution;

    for (int n = 1; n < 8; n++){
        float i = float(n);
        coord += vec2(0.7 / i * sin(i * coord.y + osg_FrameTime + 0.3 * i) + 0.8, 0.4 / i * sin(coord.x + osg_FrameTime + 0.3 * i) + 1.6);
    }

    coord *= vec2(0.7 / sin(coord.y + osg_FrameTime + 0.3) + 0.8, 0.4 / sin(coord.x + osg_FrameTime + 0.3) + 1.6);

    vec3 color = vec3(0.5 * sin(coord.x) + 0.5, 0.5 * sin(coord.y) + 0.5, sin(coord.x + coord.y));

//    gl_FragColor = vec4(color, 1.0);
    outColor = vec4(color, 1.0);
}

Result:

It works on the 3D model, but I want to use it on the camera. When the camera use this effect it can let all the model with waterColor shader. Do you how to make it?

I try to use the shader on my camera, but the result is only the waterColor shader on the screen. I guess the 3D model is covered by the shader.

Here is the code I try on the camera:

import sys

from direct.filter.FilterManager import FilterManager
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Shader, loadPrcFileData, NodePath, PandaNode, ShaderAttrib, Texture

configVars = """
win-size 720 480
show-frame-rate-meter 1
gl-version 3 2
textures-power-2 none
"""

loadPrcFileData("", configVars)


class MyApp(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        # quit when esc is pressed
        self.accept('escape', sys.exit)

        self.cam.setPos(0,10,0)

        # base.disableMouse()

        self.jack = self.loader.loadModel("jack")
        self.jack.reparentTo(self.render)
        self.jack.setScale(2.0, 2.0, 2.0)
        self.jack.setPos(8, 50, -10)

        self.panda = self.loader.loadModel("panda")
        self.panda.reparentTo(self.render)
        self.panda.setScale(2.0, 2.0, 2.0)
        self.panda.setPos(0, 80, -20)

        my_shader = Shader.load(Shader.SL_GLSL,
                                vertex="shaders/waterColor-v.glsl",
                                fragment="shaders/waterColor-f.glsl")

        manager = FilterManager(self.win, self.cam)
        tex = Texture()
        quad = manager.renderSceneInto(colortex=tex)
        quad.setShader(my_shader)

        quad.set_shader_input("resolution", (1920, 1080))

        quad.setShaderInput("tex", tex)



    def win_resize(self):
        print("resize")

if __name__ == '__main__':
    app = MyApp()
    app.run()

Thank you for reading this long post. :clap: :clap: :clap:

You might also look at the “List of CG Inputs”–while those are intended for CG, they’re also available in GLSL, I’ve found, and there may be some inputs on the CG list that aren’t given on the GLSL list.

Pretty much, I imagine: You’re applying the shader to the output quad, and the shader doesn’t refer to whatever has already been rendered (via the texture provided by the off-screen buffer), and so that rendering is simply ignored.

Now, you ask how to apply your shader to the camera. But it occurs to me that perhaps another approach might be simpler: why not apply your shader to the entire scene? That should result in it being rendered on all of your various objects.

Now, it might be tedious to apply your shader to each object individually–so instead, I suggest applying it to the root of the scene-graph (which I presume is the node called “render”). This should result in it being applied to all children of that root, as long as they don’t have a shader of their own applied.

1 Like

Thank you for the reply.
I am trying to replace the shader’s error input with the Panda3D shader input. It seems to work. Thank you. :+1:
I’m don’t know how to apply to all children. Can you say more or provide some examples? Is it something self.render.obj.setShader ?

But I have some results about applying shaders to the camera.
main.py:

import sys

from direct.filter.FilterManager import FilterManager
from direct.showbase.ShowBase import ShowBase
from panda3d.core import Shader, loadPrcFileData, NodePath, PandaNode, ShaderAttrib, Texture

configVars = """
win-size 720 480
show-frame-rate-meter 1
gl-version 3 2
textures-power-2 none
"""

loadPrcFileData("", configVars)


class MyApp(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        # quit when esc is pressed
        self.accept('escape', sys.exit)

        self.cam.setPos(0,10,0)

        # base.disableMouse()

        self.jack = self.loader.loadModel("jack")
        self.jack.reparentTo(self.render)
        self.jack.setScale(2.0, 2.0, 2.0)
        self.jack.setPos(8, 50, -10)

        self.panda = self.loader.loadModel("panda")
        self.panda.reparentTo(self.render)
        self.panda.setScale(2.0, 2.0, 2.0)
        self.panda.setPos(0, 80, -20)

        image_shader = Shader.load(Shader.SL_GLSL,
                                vertex="shaders/image-v.glsl",
                                fragment="shaders/image-f.glsl")

        manager = FilterManager(self.win, self.cam)
        tex = Texture()
        quad = manager.renderSceneInto(colortex=tex)
        quad.setShader(image_shader)

        quad.set_shader_input("resolution", (1920, 1080))

        quad.setShaderInput("tex", tex)


    def win_resize(self):
        print("resize")

if __name__ == '__main__':
    app = MyApp()
    app.run()

image-v.glsl:

#version 330

// Uniform inputs
uniform mat4 p3d_ModelViewProjectionMatrix;

// Vertex inputs
in vec4 p3d_Vertex;
in vec2 p3d_MultiTexCoord0;

// Output to fragment shader
out vec2 texcoord;

void main() {
  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
  texcoord = p3d_MultiTexCoord0;
}

image-f.glsl:

#version 330

uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;

uniform sampler2D u_tex0;

// testing

uniform vec2 resolution;
uniform float osg_FrameTime;

out vec4 outColor;

uniform sampler2D p3d_Texture0;

in vec2 texcoord;



void main(){
  vec2 coord = gl_FragCoord.xy / resolution;
  vec3 color = vec3(0.0);

  vec4 image = texture(p3d_Texture0,texcoord);
  image.r+=0.3;
  image.b += sin(osg_FrameTime);
  image.g+=cos(osg_FrameTime);

  outColor = vec4(image);
}

result:

It will change the color time by time. I’m not sure this program works properly. I want to change the 3d model not include the background.

Thank you for reading this post.

1 Like

You’re close: it’s just as follows: self.render.setShader(myShader)

Remember: things done to a node affect all of its children (unless something is done to a child to override this). And since the entire 3D scene is (by default) a child of “render”, changes to “render” should affect everything in the 3D scene.

1 Like

Thank you for the reply. I will try it later. :smiley:
May I ask another question?

I’m gonna input the mouse position into the shader. But I’m not sure how to do it.
Here is my code:

from direct.showbase.ShowBase import ShowBase
from panda3d.core import loadPrcFileData
from panda3d.core import Shader

configVars = """
win-size 1280 720
show-frame-rate-meter 1

gl-version 3 2
textures-power-2 none
"""

loadPrcFileData("", configVars)


class MyGame(ShowBase):
    def __init__(self):
        super().__init__()
        self.set_background_color(0, 0, 0, 1)
        # self.wireframeOn()
        self.cam.setPos(0, -4, 0)

        
        mouse_shader = Shader.load(Shader.SL_GLSL,
                                    vertex="shaders/mouse-v.glsl",
                                    fragment="shaders/mouse-f.glsl")

        self.jack = self.loader.loadModel("jack")

        self.jack.reparentTo(self.render)
        self.jack.setShaderInput("myMouse",self.mouseWatcherNode.getMouse())
        self.jack.set_shader_input("resolution", (1920, 1080))

        self.jack.setShader(my_shader)


game = MyGame()
game.run()

mouse-v.glsl:

#version 330

// Uniform inputs
uniform mat4 p3d_ModelViewProjectionMatrix;

// Vertex inputs
in vec4 p3d_Vertex;
in vec2 p3d_MultiTexCoord0;

// Output to fragment shader
out vec2 texcoord;

void main() {
  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
  texcoord = p3d_MultiTexCoord0;
}

mouse-f:

#version 330

uniform vec2 resolution;
uniform float osg_FrameTime;

out vec4 outColor;

uniform sampler2D p3d_Texture0;

in vec2 texcoord;

uniform mat4 p3d_ViewMatrixInverse;
uniform vec4 mspos_myMouse;

void main(){
  vec2 coord = gl_FragCoord.xy;
  vec3 color = vec3(0.0);

  color.r = mspos_myMouse.x *0.1;

  outColor = vec4(color, 1.0);
}

Thank for reading this post.

Hmm… Okay, I see two problems:

First, the result of “getMouse” is, I think, a Point2, not a NodePath. Thus constructs like “mspos_” won’t work with it, I daresay.

And second, because of this, the value that you have in your shader will–I imagine, at least–not update as the program continues–you’ll just have the mouse-position as of the point in time at which the shader-input was set. To deal with this, I might suggest using a task to update the shader-input.

1 Like

Thank you for the reply.
I think that may not what I want. I check the GLSL list it seems not to provide a mouse position uniform. I don’t want to set a task manager to keep updating the mouse position.
I really appreciate you trying to fix my problem, that gives me the motivation to learn glsl shader.
Btw, I check the GLSL list and find out something interesting thing.

vect

#version 330

in vec4 p3d_Vertex;
in vec2 p3d_MultiTexCoord0;

out vec2 texcoord;


uniform mat4 p3d_ModelViewProjectionMatrix;
uniform float osg_FrameTime;


void main() {
//    gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;

    vec4 pos = p3d_Vertex;

    pos.x += sin(osg_FrameTime + pos.y + pos.z) * 0.5;
    pos.y += sin(osg_FrameTime + pos.x + pos.z) * 0.5;
    pos.z += cos(osg_FrameTime + pos.z + pos.x) * 0.5;

    gl_Position = p3d_ModelViewProjectionMatrix * pos;
    texcoord = p3d_MultiTexCoord0;
}

frag

#version 330

in vec2 texcoord;

out vec4 outColor;


uniform vec2 resolution;
uniform float osg_FrameTime;
uniform sampler2D p3d_Texture0;



void main(){

    vec2 screenCoord = (gl_FragCoord.xy / resolution);

    vec4 color = texture(p3d_Texture0, texcoord);
    color.r += cos(osg_FrameTime) *0.5;
    color.b += sin(osg_FrameTime) *0.5;
    color += cos(screenCoord.x * 5.0 + sin(osg_FrameTime + screenCoord.y * 90.0 + cos(screenCoord.x * 10.0 + osg_FrameTime * 3.0))) * 0.5;

    outColor = color;
}

Correct; to the best of my knowledge, there is no such engine-provided uniform.

Hmm… I’m not sure that I see another way as things stand, however.

You’ve said that you want to get the position of the mouse and provide it to your shader. And since the position of the mouse changes from frame to frame, and since no engine-provided facility to handle this is (as far as I’m aware) available, doing so would seem to call for updating your shader-input on each frame.

You can perhaps put in a request (on GitHub) for a new engine-provided input, of course.

There are some neat inputs available, I do think!