Per Pixel Lighting

I’ve been trying to get Per Pixel Lighting to work for 3d objects in Panda. Is there anyone who’s tried this? I can get this working just fine in OpenGL as well as Direct3D.

Check the updated section on the manual on the Cg bugs. If that doesn’t help, Ive been able to implement it and can post the code.

Wow, if you’ve already implemented it in Panda thats great. Could you post the code? Also how are you storing the Tangent and BiNormal vectors? I’ve been able to do this in OpenGL, but have had to store in the texture channel 1 and 2 for T and B respectively with T and B precomputed in Max? Are you doing it on the fly?

I’ll have to admit that Im a new comer when its come to Cg, but I do think that I’ve implemented it correctly (fingers crossed).

This code is pretty much from the chapter in the Cg Turorial that talks about lighting.

The Vertex Shader


//just passes the ObjectSpcae position to the fragment shader
//not the most efficient but simpler to implement
void main(float4 position : POSITION,
		float3 normal : NORMAL,
		
		out float4 oPosition : POSITION,
		out float3 objectPos : TEXCOORD0,
		out float3 oNormal   : TEXCOORD1,

		uniform float4x4 modelViewProj)
{
	oPosition=mul(modelViewProj, position);
	objectPos=position.xyz;
	oNormal=normal;
}

Fragement Shader


//just passes the ObjectSpcae position to the fragment shader
//not the most efficient but simpler to implement
void main(float4 position : POSITION,
		float3 normal : NORMAL,
		
		out float4 oPosition : POSITION,
		out float3 objectPos : TEXCOORD0,
		out float3 oNormal   : TEXCOORD1,

		uniform float4x4 modelViewProj)
{
	oPosition=mul(modelViewProj, position);
	objectPos=position.xyz;
	oNormal=normal;
}

and the panda program that implements these.
Note: you have to press ‘a’ to start the shader. Its there for debugging only.
Also i dont think ‘lombard’ is a standard model so you may have to change that with another one.


#This program test lighting effects using CgShaders
#using a simplified lighting model
#This right now is really meant as more of a code sample
#than a  tutorial. Look at the appropriate parts
#of the manual for a deep explanation of whats going on here.
#Feel free to mess around with the code (like change lightpostions
#or something)

#NOTE: since the monster starts at 0,0,0 you will have to use
#the mouse to back out of it. This was done so rotation would
#be easy.


import direct.directbase.DirectStart
from direct.showbase import DirectObject
from direct.interval.IntervalGlobal import*
from pandac.PandaModules import *



myShader=CgShader("light test", "perFLightVertex.cg", "perFLightFrag.cg")
myShaderAttrib=CgShaderAttrib.make(myShader)


#this section we just pass a lot of parameters
myShader.addParam("modelViewProj", "modelViewProj", CgShader.PMATRIX, CgShader.BFRAME, 1)
myShader.setParam("modelViewProj", CgShader.MTXMODELVIEWPROJECTION, CgShader.TRFIDENTITY)

myShader.addParam("lightPosition", "lightPosition", CgShader.P3F, CgShader.BFRAME, 0)
myShader.setParam("lightPosition", Vec3(100,0,0))

myShader.addParam("eyePosition","eyePosition", CgShader.P3F, CgShader.BFRAME, 0)
myShader.setParam("eyePosition", Vec3(camera.getX(),camera.getY(),camera.getZ()))

print camera.getPos()

myShader.addParam("lightColor", "lightColor", CgShader.P3F, CgShader.BFRAME, 0)
myShader.setParam("lightColor", Vec3(1, 1, 1))

myShader.addParam("globalAmbient", "globalAmbient", CgShader.P3F, CgShader.BFRAME, 0)
myShader.setParam("globalAmbient", Vec3(0,0,0))

myShader.addParam("Ke", "Ke", CgShader.P3F, CgShader.BFRAME, 0)
myShader.setParam("Ke", Vec3(0,0,0))

myShader.addParam("Ka", "Ka", CgShader.P3F, CgShader.BFRAME, 0)
myShader.setParam("Ka", Vec3(1,1,1))

myShader.addParam("Ks", "Ks", CgShader.P3F, CgShader.BFRAME, 0)
myShader.setParam("Ks", Vec3(0.9,0.9,0.9))

myShader.addParam("Kd", "Kd", CgShader.P3F, CgShader.BFRAME, 0)
myShader.setParam("Kd", Vec3(0.5,0.5,0.5))

myShader.addParam("shininess", "shininess", CgShader.P1F, CgShader.BFRAME, 0)
myShader.setParam("shininess", 8)

monster=loader.loadModel('lombard')
monster.reparentTo(render)
monster.setPos(0,0,0)

#need to setup lights to make panda calculate normals
#see Cg bugs section in manual
dlight=DirectionalLight('dlight')
dlight.setColor(VBase4(0.2, 0.4, 0.9, 1))
dlnp=render.attachNewNode(dlight.upcastToPandaNode())

dlnp.lookAt(monster)

render.setLight(dlnp)


#we need another object in the scene to make
#panda act normal
#see cg bugs section in Panda  Manual
teapot=loader.loadModel('teapot')
teapot.reparentTo(render)
teapot.setPos(monster, 0, 0, 0)
teapot.setScale(0.01)


class myTapper(DirectObject):
	def __init__(self):
		self.accept('a', self.startShader)
		self.accept('s', self.removeAttrib1)
		self.accept('r', self.redLight)
		self.accept('g', self.greenLight)
		self.accept('b', self.blueLight)
	def startShader(self):
		global monster
		monster.node().setAttrib(myShaderAttrib)
	def removeAttrib1(self):
		global monster, myShaderAttrib
		monster.node().clearAttrib(CgShaderAttrib.getClassType())
	def redLight(self):
		global myShader
		myShader.setParam("lightColor", Vec3(1,0,0))
	def greenLight(self):
		global myShader
		myShader.setParam("lightColor", Vec3(0,1,0))
	def blueLight(self):
		global myShader
		myShader.setParam("lightColor", Vec3(0,0,1))


t=myTapper()



run()

Hope this helps.

It seems you’ve posted the vertex shader twice.

heres the fragment shader


//implements really simple lighting based on a simplified
//version of OpenGl's and DirectX's fixed-point lighting model
void main(float4 position : TEXCOORD0,
	float3 normal : TEXCOORD1,
	
	out float4 color : COLOR,
	
	uniform float4x4 modelViewProj,
	uniform float3 globalAmbient,
	uniform float3 lightColor,
	uniform float3 lightPosition,
	uniform float3 eyePosition,
	uniform float3 Ke,
	uniform float3 Ka,
	uniform float3 Kd,
	uniform float3 Ks,
	uniform float shininess)
{
	
	
	
	float3 P=position.xyz;
	float3 N=normalize(normal);

	float3 emissive =Ke;
	
	float3 ambient=Ka*globalAmbient;

	float3 L=normalize(lightPosition - P);
	float diffuseLight = max(dot(N,L),0);
	float3 diffuse=Kd*lightColor*diffuseLight;

	float3 V = normalize(eyePosition - P);
	float3 H = normalize(L+V);
	float specularLight = pow(max(dot(N,H), 0), shininess);	
	if (diffuseLight == 1 ) specularLight = 0;
	float3 specular=Ks*lightColor*specularLight;

	color.rgb=emissive+ambient+diffuse+specular;
	color.a=1;
}

The current version of panda does not provide tangents and binormals, but I’m told that the next version will.

  • Josh

So you don’t actually need panda to supply you with anything because you can either calculate it in the vertex shader or precompute it in max or maya and store it within a texcoord channel. The easiest way I can think of (and probably what I’m going to implement) when I get back to the states is to calculate T (tangent) using maxscript and calculate B on the fly.

That works for static meshes, but if the model is animated, you need to update the tangents and binormals as the model moves.

Yeah your diffinitely right. If its a character mesh, then things tend to get messed up. I was thinking of using normal maps for a room for precalculated TNB matrix.

In terms of getting working on characters, which I’m also looking into, it seems that certain profiles for cg support Tangents and Binormals calculated by the graphics card (if I’m reading docs correctly :blush: ).