Shader camera question

I’m trying some shader effect on the panda3D. But I don’t know how to implement it into panda3D.

Here is the main program:

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
"""

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/vignette-vert.glsl",
                                fragment="shaders/vignette-frag.glsl")

        self.plane = self.loader.loadModel("my-models/plane")
        self.plane.reparentTo(self.render)
        self.plane.set_shader_input("resolution", (self.win.getXSize(), self.win.getYSize()))
        self.plane.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()

vignette-vert.glsl

#version 330

// Vertex inputs
in vec4 p3d_Vertex;


// Uniform inputs
uniform mat4 p3d_ModelViewProjectionMatrix;


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

vignette-frag.glsl

#version 330

uniform vec2 resolution;

out vec4 outColor;

const float outerRadius = 0.65;
const float innerRadius = 0.3;
const float intensity = 0.6;

// can be created on one line
// const float outerRadius = 0.65, innerRadius = 0.3, intensity = 0.6;

void main() {

    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, intensity);

    outColor = color;
}

result:

reference:GitHub - totex/Panda3D-shaders: Using GLSL shaders in Panda3D.

I read the Shaders and Coordinate Spaces, and I try to use render.setShaderInput("shadowcam", shadowcam) make a shader camera. However, it doesn’t work.
https://docs.panda3d.org/1.10/python/programming/shaders/coordinate-spaces

Here is my program:

import subprocess
import sys

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

configVars = """
win-size 1280 720
show-frame-rate-meter 1
gl-version 3 2
"""

loadPrcFileData("", configVars)


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

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

        # base.disableMouse()

        self.jack = self.loader.loadModel("panda")
        self.jack.reparentTo(self.render)
        self.jack.setScale(2.0, 2.0, 2.0)
        self.jack.setPos(8, 50, 0)
        self.jack.setHpr(180,180,180)

        my_shader = Shader.load(Shader.SL_GLSL,
                                vertex="shaders/vignette-vert.glsl",
                                fragment="shaders/vignette-frag.glsl")
        # my_shader = Shader.load(Shader.SL_GLSL,
        #                         vertex="shaders/gradient-vert.glsl",
        #                         fragment="shaders/gradient-frag.glsl")
        # my_shader = Shader.load(Shader.SL_GLSL,
        #                         vertex="shaders/myshader-v.glsl",
        #                         fragment="shaders/myshader-f.glsl")

        # self.jack.setShader(my_shader)
        self.cam.setShader(my_shader)
        # self.render.setShaderInput("jack",my_shader)

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

Please let me know if you have some shaders tutorials or samples.
Thank you for reading this post.

It’s not entirely clear to me what you’re trying to do–would you mind clarifying, please?

From your screenshot and the name of your shader, my impression is that you want to apply an effect to the output of the camera. But conversely, you mention attempting to provide to your shader a shader-input called “shadowcam”, which implies that you’re attempting to implement shadow-mapping…

Thank you for the reply.
Sorry, I misread shadow camera as shader camera. I thought shadowcam is shadercam. I want to make a camera effect to imitate the camera lens.
Like this: FishEye Lens distortion (bug?) - Graphics and GPU Programming - GameDev.net

However, I don’t know how to implement the shader effect to the camera.
Therefore, I try to change the Shader Basics code to:

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

        self.cam.setShader(my_shader)

I’m new to shader, and I’m not sure my shader is usable on camera.
Please let me know if there is something you don’t understand or if I have done something wrong.

Ah, okay! I thought that it might be so, but I wasn’t confident.

Okay, that can be done, I think, by applying the shader to the “initial state” of the camera. Like so:

tempNP = NodePath(PandaNode("camera initialiser"))

tempNP.setShader(myShader)
tempNP.setShaderInput("someInput", someValue)

base.cam.node().setInitialState(tempNP.getState())

tempNP.removeNode()

(You could also render your scene to an off-screen buffer, use that buffer to texture a card, and then apply your shader to said card, if I’m not much mistaken.)

1 Like

What you actually want to do is not apply a shader to the camera, but render the scene to a texture, then apply that texture to a fullscreen quad, on which the shader is applied. That allows the shader to manipulate the pixels of the rendered image.

This process is automated by FilterManager. See this page for more details:
https://docs.panda3d.org/1.10/python/programming/render-to-texture/generalized-image-filters

2 Likes

I am very grateful for Thaumaturge and rdb replies. Please feel free to let me know if I doing something wrong.

To make sure nothing wrong with the shader. I use the panda3D example shader and my shader vignette.

Here is the code:

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
"""

loadPrcFileData("", configVars)


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

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

        # 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)

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

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

        # Thaumaturge
        # tempNP = NodePath(PandaNode("camera initialiser"))
        #
        # tempNP.setShader(my_shader)
        # tempNP.setShaderInput("someInput", (0.5,0.5,0.5,0.5))
        #
        # self.cam.node().setInitialState(tempNP.getState())
        # tempNP.removeNode()


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



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

Here is the result:

I don’t know the Pand3D shader looks like. Thaumaturge’s method seems correct? rdb’s method reduced the screen size.

I guess is something wrong with the vignette shader.
Here is the error msg when using Thaumaturge and rdb method:

Assertion failed: Shader input resolution is not present.
 at line 397 of panda/src/pgraph/shaderAttrib.cxx
Traceback (most recent call last):
  File "/Users/opt/anaconda3/envs/py310/lib/python3.10/site-packages/direct/showbase/ShowBase.py", line 2156, in __igLoop
    self.graphicsEngine.renderFrame()
AssertionError: Shader input resolution is not present.
 at line 397 of panda/src/pgraph/shaderAttrib.cxx
:task(error): Exception occurred in PythonTask igLoop
Traceback (most recent call last):
  File "/Users/gitdir/fyp/panda3d/other/test_init.py", line 66, in <module>
    app.run()
  File "/Users/opt/anaconda3/envs/py310/lib/python3.10/site-packages/direct/showbase/ShowBase.py", line 3328, in run
    self.taskMgr.run()
  File "/Users/opt/anaconda3/envs/py310/lib/python3.10/site-packages/direct/task/Task.py", line 553, in run
    self.step()
  File "/Users/opt/anaconda3/envs/py310/lib/python3.10/site-packages/direct/task/Task.py", line 504, in step
    self.mgr.poll()
  File "/Users/opt/anaconda3/envs/py310/lib/python3.10/site-packages/direct/showbase/ShowBase.py", line 2156, in __igLoop
    self.graphicsEngine.renderFrame()
AssertionError: Shader input resolution is not present.
 at line 397 of panda/src/pgraph/shaderAttrib.cxx

I don’t understand what’s wrong with the vignette shader. It works on the 3D model.
I will try more glsl shaders to learn to implement shaders in the camera.

Actually, it looks like rdb’s solution is producing the correct output: Based on the page to which you linked, the result should be the original output, but with the red and blue channels swapped–i.e. with redness becoming blueness and vice versa. And indeed, that does seem to be what we see under rdb’s solution.

I’m not sure of why the image is squashed under rdb’s solution–I’m actually have a similar setup in a project of mine, and the output has the expected dimensions there.

(And, having been reminded by rdb’s post, let me affirm that rdb’s method is likely the way to go about this.)

It looks like you’re simply missing a shader-input: in your Python-code, you haven’t provided the shader with something called “resolution”.

1 Like

Thank you for the reply.

I use the method you provide. The shader works. :+1:
However, the resolution is squashed.

I’m thinking is it about the Resolution Management. And I change renderSceneInto to renderQuadInto.
However, it doesn’t work.
Here is the code I try:

#rdb
        manager = FilterManager(self.win, self.cam)
        tex = Texture()
        # quad = manager.renderSceneInto(colortex=tex,resolution=(self.win.getXSize(),self.win.getYSize()))
        quad = manager.renderQuadInto(colortex=tex)

        quad.setShader(my_shader)
        quad.setShaderInput("tex", tex)

Do you know how to fix the resolution?

Set textures-power-2 none in Config.prc.

2 Likes