Getting camera position and direction of current frame in shader

Hello,
I have been working on a glsl shader that requires the camera position and direction of every frame in order to display some shapes on the pixels that appear to be 3D. However I couldn’t find any shader input from Panda3d that gives directly camera position and camera direction, so I tried to create them using shader translation matrices.

I am fairly new to the world of shaders, so I took a trial-and-error approach. The closest I managed to get to finding the camera position was by using the trans_view_to_world matrix to transform my vector and then exchanging the y with the z components of the vector. Here’s what it looks like in my code

vec4 camera_start =  vec4(0.,0.,0.,1.);
vec4 camera_pos  =  trans_view_to_world * camera_start;
vec3 cp = vec3(camera_pos_4[0] , camera_pos_4[2] , camera_pos_4[1] ); 
return cp; // current camera position

Though this gives results that are seemingly correct (zooming in and out and panning seem to work rather well), the result is off. When the camera rotates the shapes shoot off in an unexpected direction and it always feels that they are further away from the camera that they should be.

Any ideas on how I can find the correct camera position would be very welcome, thanks a lot!

Hmm… The following itself won’t allow for things like zooming, I think, but if you just want the camera’s position and orientation, you should be able to get that by accessing its NodePath via a shader-input.

In your code, you would call the “setShaderInput” method of the NodePath to which the shader had been applied, passing in the name that you want the camera to have in your shader, and the camera-NodePath itself. Like this:

self.myShadedNodePath.setShaderInput("myCamera", base.camera)

(I think that “base.camera” is the correct variable here; if not, it might be “base.cam”.)

Then, in your shader, you would be able to create shader-inputs that draw on it, referencing it by the name specified in “setShaderInput”.

For example, to get the model-space position of the camera, you might add the following to your shader:

uniform vec4 mspos_myCamera;

(“mspos” indicates the “model-space position”. See the lists of shader inputs, below, for more options!
https://www.panda3d.org/manual/?title=List_of_GLSL_Shader_Inputs
https://www.panda3d.org/manual/?title=List_of_Possible_Cg_Shader_Inputs
(I think that the Cg inputs are available in GLSL, too.))

Note, by the way, that this isn’t specific to cameras: you can do this with any NodePath, I believe!

Hello, thanks a lot for your answer! I tried that but the problem is that this only sets up the camera at the initial position and orientation, but doesn’t update when I move around the scene. Instead, I would need it to update at runtime.

You need to use set_shader_input in the task.

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

class ShaderDemo(ShowBase):
    def __init__(self):

        ShowBase.__init__(self)
        
        shader = Shader.load(Shader.SL_GLSL, "vert.glsl", "frag.glsl")

        self.model = self.loader.loadModel("box")
        
        self.model.reparent_to(self.render)
        
        self.model.set_shader(shader)

        taskMgr.add(self.update, "update")

    def update (self, task):
    
        self.model.set_shader_input("pos", camera.get_pos());

        return task.cont

ShaderDemo().run()
1 Like

Hmm… That’s odd, because I’m pretty sure that I’ve done exactly that and had it update as it runs.

Could you show the code that you used, please? Perhaps there’s an error that’s preventing it from working.

(Unless someone who knows better than I corrects me, of course!)

It appears that the trick was to set_shader_input in the task manager. No my task looks like that:

def update (self, task):
    self.nodePath.set_shader_input("camera_POS", self.camera.getPos());
    self.nodePath.set_shader_input("camera_HPR", self.camera.getHpr());
    return task.cont    

Thank you both for your answers! You were very helpful.