Thanks for response!
I can mult them in shader, but I will need to pass several matrices (light projection, light view etc.) to shader instead of one.
Right now I have good looking shadows… but they very strange , run code to see.
Thats my code:
import sys
from direct.showbase.ShowBase import ShowBase
from pandac.PandaModules import *
from direct.showbase import DirectObject
from panda3d.core import DirectionalLight, PointLight, Spotlight
from direct.interval.IntervalGlobal import *
def get_gl_projection_mat(lens):
gl_proj_mat=Mat4.convertMat(CSYupRight, lens.getCoordinateSystem()) * lens.getProjectionMat()
# uncomment next line if matrix passed to shader with transform_mat call
# gl_proj_mat.transposeInPlace()
return gl_proj_mat
def get_gl_modelview_mat(node, camera):
cs_transform=TransformState.makeMat(Mat4.convertMat(base.win.getGsg().getInternalCoordinateSystem(), CSZupRight))
return cs_transform.invertCompose(node.getTransform(camera)).getMat()
class Poser(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.setup_models()
self.setup_light()
self.render.setShaderAuto()
self.accept('escape', sys.exit)
self.accept('o', self.oobe)
base.disableMouse()
base.camera.setPos(5, -20, 25)
base.camera.lookAt(self.actor)
base.setBackgroundColor(VBase4(0.5, 0.5, 0.5, 1.0))
# make FBO
winprops = WindowProperties.size(512, 512)
props = FrameBufferProperties()
props.setRgbColor(0)
props.setAlphaBits(0)
props.setDepthBits(1)
LBuffer = base.graphicsEngine.makeOutput(
base.pipe, "offscreen buffer", -2,
props, winprops,
GraphicsPipe.BFRefuseWindow,
base.win.getGsg(), base.win)
base.bufferViewer.toggleEnable()
# render to texture
Ldepthmap = Texture()
LBuffer.addRenderTexture(Ldepthmap, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepth)
# clamp
Ldepthmap.setWrapU(Texture.WMClamp)
Ldepthmap.setWrapV(Texture.WMClamp)
# make depth camera
self.LCam = base.makeCamera(LBuffer)
self.LCam.node().setScene(render)
# copy lens from light and reparent
self.LCam.node().setLens(self.light.node().getLens())
self.LCam.node().showFrustum()
self.LCam.reparentTo(self.light)
# hide floor from LCam so it wont self-shadow
self.LCam.node().setCameraMask(BitMask32.bit(1))
self.floor.hide(BitMask32.bit(1))
# get projection and model top view matrices for LCam
# code is based on panda C++ sources for passing matrices to CG shaders.. hope not errors in my C->Python translation
light_proj=get_gl_projection_mat(self.LCam.node().getLens())
light_view=get_gl_modelview_mat(base.render, self.LCam)
# texture projection matrix
tx_mat = light_view * light_proj
# set shader inputs
self.floor.setShaderInput("lproj[0]", tx_mat)
self.floor.setShaderInput("lproj", tx_mat)
self.floor.setShaderInput("shadowMap", Ldepthmap)
sh1 = Shader.load(Shader.SLGLSL, "shaders/vshader_1.glsl", "shaders/fshader_1.glsl")
self.floor.setShader(sh1)
def setup_models(self):
self.actor = loader.loadModel("box")
self.actor.setDepthOffset(1)
self.actor.reparentTo(render)
self.actor.setScale(2)
self.actor.setPos(-5, 0, 2)
self.actor.setP(-45)
self.actor1 = loader.loadModel("panda")
self.actor1.setDepthOffset(1)
self.actor1.reparentTo(render)
self.actor1.setPos(-1, -2, 5)
self.actor1.setP(-45)
self.actor1.setScale(0.5)
floorTex = loader.loadTexture('maps/envir-ground.jpg')
cm = CardMaker('')
cm.setFrame(-2, 2, -2, 2)
self.floor = render.attachNewNode(PandaNode("floor"))
for y in range(12):
for x in range(12):
nn = self.floor.attachNewNode(cm.generate())
nn.setP(-90)
nn.setPos((x - 6) * 4, (y - 6) * 4, 0)
self.floor.setTexture(floorTex)
self.floor.setScale(2)
self.floor.flattenStrong()
def setup_light(self):
alight = AmbientLight('alight')
alight.setColor(VBase4(0.1, 0.1, 0.1, 1))
alnp = render.attachNewNode(alight)
render.setLight(alnp)
plight = Spotlight('plight')
plight.showFrustum()
plight.setColor(VBase4(1.0, 1.0, 1.0, 1))
plnp = render.attachNewNode(plight)
plnp.setPos(0, 0, 50)
plnp.setHpr(0, -90, 0)
plight.setAttenuation(Point3(1, 0, 0))
render.setLight(plnp)
plight.getLens().setFov(65)
plight.getLens().setNearFar(1, 100)
self.light = plnp
# rotate light
hpr=self.light.getHpr()
m1 = LerpHprInterval(self.light, 10, Vec3(hpr[0], -60, hpr[2]))
m2 = LerpHprInterval(self.light, 10, Vec3(hpr[0], hpr[1], hpr[2]))
move = Sequence(m1, m2, name="move_light1")
move.loop()
p = Poser()
p.run()
Vertex shader (shaders/vshader_1.glsl):
attribute vec2 p3d_MultiTexCoord0;
attribute vec4 p3d_Vertex;
attribute vec3 p3d_Normal;
varying vec2 texcoord0;
varying vec3 normal;
varying vec3 lightDir;
varying vec4 ShadowCoord;
uniform vec4 lproj[4];
const mat4 biasMatrix = mat4( 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0);
void main() {
gl_Position = gl_ModelViewProjectionMatrix * p3d_Vertex;
texcoord0 = p3d_MultiTexCoord0;
normal = gl_NormalMatrix * p3d_Normal;
lightDir = vec3(gl_LightSource[0].position) - vec3(gl_ModelViewMatrix * p3d_Vertex);
mat4 sh_mat=mat4(lproj[0], lproj[1], lproj[2], lproj[3]);
ShadowCoord = biasMatrix * sh_mat * p3d_Vertex;
}
Fragment (shaders/fshader_1.glsl):
#version 110
varying vec2 texcoord0;
varying vec3 normal;
varying vec3 lightDir;
// modulate texture
uniform sampler2D p3d_Texture0;
uniform sampler2DShadow shadowMap;
varying vec4 ShadowCoord;
void main() {
vec3 N = normalize(normal);
vec3 L = normalize(lightDir);
vec4 tex = texture2D(p3d_Texture0, texcoord0.st);
float NdotL = clamp(dot(N, L), 0.0, 1.0);
float shadow = 1.0;
if (ShadowCoord.w > 1.0) shadow = clamp(shadow2DProj(shadowMap, ShadowCoord).z + 0.5, 0.5, 1.0);
vec4 finalColor = vec4 ( 0.05, 0.05, 0.05, 1.0 ) + vec4(tex.rgb * NdotL, 1.0) * shadow;
gl_FragColor = finalColor;
}
I have used light_viewlight_projp3d_Vertex as texture matrix.
Initially I tried to pass it to shader with PTA_LVecBase4f/UnalignedLVecBase4f, but found what passing Mat4 works excellent for me!
To confirm this, I converted C+ code for composing OpenGL gl_ModelView, gl_Projection matrices, passed them to vshader as uniform vec4 mat[4] and convert p3d_Vertex with them and not panda3d prepared - result image stays the same, so I assume I got it correctly.
Now 2 questions remains:
- Why are shadows drawn in wrong direction? I think still error in my matrix math, as I dont use model_to_world transform, and I looking on this now.
- I have setup shader inputs once, but shadows moves correctly as I rotate light! I thought I need to update inputs each time light moves/rotate… Why it happens? Just because FBO changes?
- Maybe I am looking in worng direction at all and there is a ways to pass CG supported inputs to GLSL shaders? I have tried with passing nodepath, but it dont work for me.