I’ve seen a few people ask for it, so I put a snippet here. It is per vertex lighting, so the specular will be a bit weird. The shadows are parallel I think.
http://www.sendspace.com/file/r092xo
It needs panda3D devel version.
It wont work on GM45
EDIT : added per pixel lighting + no light falloff so it looks like sun.
[color=green]PER PIXEL LIGHTING NO FALLOFF :
//Cg
// VERTEX SHADER //
void vshader(
float4 vtx_position : POSITION,
float3 vtx_normal : NORMAL,
out float4 l_position : POSITION,
out float4 l_color : COLOR,
out float4 l_shadowcoord : TEXCOORD1,
out float4 l_lightclip : TEXCOORD3,
out float4 l_modelpos : TEXCOORD4,
out float3 l_normal : TEXCOORD5,
uniform float3 k_scale,
uniform float4 k_push,
uniform float4x4 trans_model_to_clip_of_light,
uniform float4x4 mat_modelproj
)
{
float4 position = vtx_position * float4(k_scale,1);
l_position = mul(mat_modelproj, position);
l_modelpos = position;
l_normal = vtx_normal;
// calculate light-space clip position.
float4 pushed = position + float4(vtx_normal * k_push, 0);
l_lightclip = mul(trans_model_to_clip_of_light, pushed);
// calculate shadow-map texture coordinates.
l_shadowcoord = l_lightclip * float4(0.5,0.5,0.5,1.0) + l_lightclip.w * float4(0.5,0.5,0.5,0.0);
}
// PIXEL SHADER //
void fshader(
uniform sampler2D k_shadowmap : TEXUNIT3,
uniform float4 k_push,
uniform float3 k_specular,
uniform float4x4 trans_clip_to_world,
uniform float4x4 trans_model_to_world,
uniform float4x4 trans_model_to_clip_of_light,
uniform float3 k_globalambient,
uniform float3 k_lightcolor,
uniform float4 wspos_A,
uniform float4 wspos_B,
uniform float4 wspos_camera,
uniform float3 k_scale,
in float4 l_color : COLOR,
in float4 l_shadowcoord : TEXCOORD1,
in float l_smooth : TEXCOORD2,
in float4 l_lightclip : TEXCOORD3,
in float4 l_modelpos : TEXCOORD4,
in float3 l_normal : TEXCOORD5,
out float4 o_color : COLOR0)
{
float3 P; // point being lit
float3 N; // surface normal
float3 L; // light ray vector
float3 V; // vector toward the viewpoint
float3 H; // vector halfway between V and L
float diffuse_light; // amount of diffuse light per vertex
float specular_light; // amount of specular light per vertex
// transformations into world space
P = mul(trans_model_to_world, l_modelpos);
N = normalize(mul(float3x3(trans_model_to_world), l_normal));
// calculate diffuse light
L = normalize(wspos_A - wspos_B);
diffuse_light = max(dot(N, L), 0) ;
// calculate specular light
V = normalize(wspos_camera - P);
H = normalize(L + V);
specular_light = pow(max(dot(N, H), 0), k_specular.x);
if (diffuse_light <= 0) specular_light = 0;
float3 circleoffs;
float falloff;
float shade;
// calculate light falloff
circleoffs = float3(l_lightclip.xy / l_lightclip.w, 0);
falloff = saturate(1.0 - dot(circleoffs, circleoffs));
// calculate shadows projection
shade = tex2Dproj(k_shadowmap,l_shadowcoord);
// final output
o_color.xyz = diffuse_light * k_lightcolor + specular_light * k_lightcolor + k_globalambient;
if(falloff > 0)o_color *= shade;
}
[color=green]PER VERTEX LIGHTING :
//Cg
// VERTEX SHADER //
void vshader(
float4 vtx_position : POSITION,
float3 vtx_normal : NORMAL,
out float4 l_position : POSITION,
out float4 l_color : COLOR,
out float4 l_shadowcoord : TEXCOORD1,
out float4 l_lightclip : TEXCOORD3,
uniform float4 k_push,
uniform float3 k_scale,
uniform float3 k_specular,
uniform float4x4 mat_modelproj,
uniform float4x4 trans_model_to_world,
uniform float4x4 trans_model_to_clip_of_light,
uniform float3 k_globalambient,
uniform float3 k_lightcolor,
uniform float4 wspos_A,
uniform float4 wspos_B,
uniform float4 wspos_camera)
{
float4 position = vtx_position * float4(k_scale,1);
l_position = mul(mat_modelproj, position);
float3 P; // point being lit
float3 N; // surface normal
float3 L; // light ray vector
float3 V; // vector toward the viewpoint
float3 H; // vector halfway between V and L
float diffuse_light; // amount of diffuse light per vertex
float specular_light; // amount of specular light per vertex
// transformations into world space
P = mul(trans_model_to_world, vtx_position);
N = normalize(mul(float3x3(trans_model_to_world), vtx_normal));
// calculate diffuse light
L = normalize(wspos_A - wspos_B);
diffuse_light = max(dot(N, L), 0) ;
// calculate specular light
V = normalize(wspos_camera - P);
H = normalize(L + V);
specular_light = pow(max(dot(N, H), 0), k_specular.x);
if (diffuse_light <= 0) specular_light = 0;
l_color.xyz = diffuse_light * k_lightcolor + specular_light * k_lightcolor + k_globalambient;
l_color.w = 1;
// calculate light-space clip position.
float4 pushed = position + float4(vtx_normal * k_push, 0);
l_lightclip = mul(trans_model_to_clip_of_light, pushed);
// calculate shadow-map texture coordinates.
l_shadowcoord = l_lightclip * float4(0.5,0.5,0.5,1.0) + l_lightclip.w * float4(0.5,0.5,0.5,0.0);
}
// PIXEL SHADER //
void fshader(
uniform sampler2D k_shadowmap : TEXUNIT3,
in float4 l_color : COLOR,
in float4 l_shadowcoord : TEXCOORD1,
in float l_smooth : TEXCOORD2,
in float4 l_lightclip : TEXCOORD3,
out float4 o_color : COLOR0)
{
float3 circleoffs;
float falloff;
float shade;
// calculate light falloff
circleoffs = float3(l_lightclip.xy / l_lightclip.w, 0);
falloff = saturate(1.0 - dot(circleoffs, circleoffs));
// calculate shadows projection
shade = tex2Dproj(k_shadowmap,l_shadowcoord);
// final output
o_color = l_color * shade * falloff ;
}
.py :
#####################################################
## Directionnal Light Shadows ##
#####################################################
## 23/09/11
## by Manou
### IMPORTS ###
from panda3d.core import *
loadPrcFileData('', 'framebuffer-stencil 0')
loadPrcFileData('', 'compressed-textures 0')
loadPrcFileData('', 'show-buffers 0')
loadPrcFileData('', 'basic-shaders-only 1')
import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import Vec3,Vec2
from pandac.PandaModules import Lens
from math import pi,sin,cos
from direct.actor.Actor import Actor
# CONSTANTS #
LIGHT_COLOR = Vec3(1.0,0.7,0.3)
GLOBAL_AMBIANT = Vec3(.1,.1,.1)
SHADOW_MAP_SIZE = 512
FILM_SIZE = Vec2(1000,1000)
SHADOW_BIAS = Vec3(10,10,10)
class Dirlight(DirectObject):
### INIT ###
def __init__(self):
self.setup_scene()
self.setup_buffers()
self.setup_cameras()
self.setup_shaders()
taskMgr.add(self.update,"update")
### SCENE SETUP ###
def setup_scene(self):
self.scene = render.attachNewNode("scene")
# light direction A => B
self.A = loader.loadModel("sphere_1")
self.A.reparentTo(render)
self.A.setPos(-100,0,20)
self.A.setScale(1)
self.A.setColor(1,0,0)
self.B = loader.loadModel("sphere_1")
self.B.reparentTo(render)
self.B.setPos(0,0,10)
self.B.setColor(0,1,0)
self.B.setScale(1)
# ground
self.ground = loader.loadModel("grass_anim")
self.ground.reparentTo(self.scene)
self.ground.node().setBounds(OmniBoundingVolume())
self.ground.node().setFinal(1)
# cubes
self.cubes = []
for i in range(10):
angle_degrees = i * 36
angle_radians = angle_degrees * (pi / 180.0)
cube = loader.loadModel("cube")
cube.reparentTo(self.scene)
cube.setPos(sin(angle_radians) * 40, cos(angle_radians) * 40, 20)
cube.node().setBounds(OmniBoundingVolume())
cube.node().setFinal(1)
self.cubes.append(cube)
### CAMERAS SETUP ###
def setup_cameras(self):
base.cam.node().getDisplayRegion(0).setClearColor(Vec4(.5, .5, .5, 1))
base.cam.node().getDisplayRegion(0).setClearColorActive(1)
self.shadow_cam = base.makeCamera(self.shadow_buffer)
self.shadow_cam.reparentTo(self.A)
lens = OrthographicLens()
lens.setFilmSize(FILM_SIZE)
self.shadow_cam.node().setLens(lens)
self.shadow_cam.node().getDisplayRegion(0).setClearColor(Vec4(0, 0, 0, 1))
self.shadow_cam.node().getDisplayRegion(0).setClearColorActive(1)
### BUFFERS SETUP ###
def setup_buffers(self):
shadow_map_temp = Texture()
self.shadow_buffer = base.win.makeTextureBuffer('shadow', SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, shadow_map_temp)
self.shadow_map = Texture()
self.shadow_map.setMinfilter(Texture.FTShadow)
self.shadow_map.setMagfilter(Texture.FTShadow)
self.shadow_buffer.addRenderTexture(self.shadow_map, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepth)
### SHADERS SETUP ###
def setup_shaders(self):
# PARAMS : object, specular, scale #
# apply light to ground
self.apply_light(self.ground, 30, Vec3(1000,1000,1000))
# apply light to cubes
for i in range(10):
self.apply_light(self.cubes[i], 30, Vec3(2,2,100))
### LIGHT FUNCTION ###
def apply_light(self, obj, specular, scale):
# apply light shader
obj.setShader(Shader.load("dir_light.cg"))
# per object
obj.setShaderInput('specular', specular)
obj.setShaderInput('scale', scale)
# all objects
obj.setShaderInput('push', SHADOW_BIAS )
obj.setShaderInput('globalambient', GLOBAL_AMBIANT)
obj.setShaderInput('lightcolor', LIGHT_COLOR)
obj.setShaderInput('light', self.shadow_cam)
obj.setShaderInput('A', self.A )
obj.setShaderInput('B', self.B )
obj.setShaderInput('camera', base.camera)
obj.setShaderInput('shadowmap', self.shadow_map)
### UPDATE PER FRAME ###
def update(self,task):
# tests
self.A.setZ(self.A.getZ()+cos(task.time)/20)
self.A.setY(self.A.getY()+cos(task.time)/20)
# shadow cam always looking at B
self.shadow_cam.lookAt(self.B)
return task.cont
app = Dirlight()
run()