Trying to implement per pixel shading in glsl

Because I am modifying geometry in a shader I do not have the luxury of using the shader generator for features such as per pixel lighting, so I have to write them by hand. Currently my sphere is just black.

Tutorial I am working from

Here are my shaders thus far:
vertex

uniform mat4 p3d_ModelViewProjectionMatrix;
varying vec4 texCoords;

// Per pix lighting
varying vec3 varyingNormalDirection; 
            // surface normal vector in view space
        
///Per pix lighting

void main(void) {
    vec4 test = gl_Vertex;

    //By adding 1 to z we move our grid to make a cube.
    //We do 1 instead of 0.5 to make all the math easy.
    test.z = test.z + 1.0;

    //While this simple normalizing works it causes some distortion near the edges
    //While less distoration than a standard sphere there are more ideal solutions
    //Some cubemapping software will compensate for said distortions however
    gl_Position = p3d_ModelViewProjectionMatrix * vec4(normalize(test.xyz), test.w);
    
    //The normalization creates almost no edge distortion
    //Proof: http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html
    //float x = test.x;
    //float y = test.y;
    //float z = test.z;
    //test.x *= sqrt(1.0 - y * y * 0.5 - z * z * 0.5 + y * y * z * z / 3.0);
    //test.y *= sqrt(1.0 - z * z * 0.5 - x * x * 0.5 + z * z * x * x / 3.0);
    //test.z *= sqrt(1.0 - x * x * 0.5 - y * y * 0.5 + x * x * y * y / 3.0);
    //gl_Position = p3d_ModelViewProjectionMatrix * test;

    varyingNormalDirection = normalize(gl_NormalMatrix * gl_Normal);
    gl_FrontColor = gl_Color;
    texCoords = gl_MultiTexCoord0;          
}

fragment

uniform sampler2D p3d_Texture0;
varying vec2 texture_coordinate;
varying vec4 texCoords;

//
varying vec4 position; 
            // position of the vertex (and fragment) in view space 
varying vec3 varyingNormalDirection; 
            // surface normal vector in view space
//

//varying out vec4 FragColor;

void main(void)
{
    vec3 normalDirection = normalize(varyingNormalDirection);
    vec3 viewDirection = -normalize(vec3(position));
    vec3 lightDirection;
    //We will precompute sun intensity cpu side so we will not worry
    //About attenuation
    float attenuation = 1.0;

    if (0.0 == gl_LightSource[0].position.w) 
               // directional light?
            {
               lightDirection = 
                  normalize(vec3(gl_LightSource[0].position));
            } 
            else // point light or spotlight (or other kind of light) 
            {
               vec3 positionToLightSource = 
                  vec3(gl_LightSource[0].position - texCoords);
               float distance = length(positionToLightSource);
               lightDirection = normalize(positionToLightSource);
 
               if (gl_LightSource[0].spotCutoff <= 90.0) // spotlight?
               {
                  float clampedCosine = max(0.0, dot(-lightDirection, 
                     gl_LightSource[0].spotDirection));
               }
            }
 
            vec3 ambientLighting = vec3(gl_LightModel.ambient) 
               * vec3(gl_FrontMaterial.emission);
 
            vec3 diffuseReflection = attenuation 
               * vec3(gl_LightSource[0].diffuse) 
               * vec3(gl_FrontMaterial.emission)
               * max(0.0, dot(normalDirection, lightDirection));
 
            vec3 specularReflection;
            if (dot(normalDirection, lightDirection) < 0.0) 
               // light source on the wrong side?
            {
               specularReflection = vec3(0.0, 0.0, 0.0); 
                  // no specular reflection
            }
            else // light source on the right side
            {
               specularReflection = attenuation 
                  * vec3(gl_LightSource[0].specular) 
                  * vec3(gl_FrontMaterial.specular) 
                  * pow(max(0.0, dot(reflect(-lightDirection, 
                  normalDirection), viewDirection)), 
                  gl_FrontMaterial.shininess);
            }

    vec4 tex0 = texture2D(p3d_Texture0, texCoords.xy);
    gl_FragColor = vec4((ambientLighting 
               + diffuseReflection) * vec3(tex0) + specularReflection, 1.0) * gl_Color
}

The light is being set with:
from panda3d.core import PointLight
light = PointLight(‘light’)
lightnp = render.attachNewNode(light)
lightnp.set_pos(0, -10, 0)
render.set_light(lightnp)

Possible something wrong in your tutorial. I used this one ozone3d.net/tutorials/glsl_l … _phong.php and shaders from this tutorial works fine for me.

An update: I put my cubic sphere and the handy smiley in the same scene. The current incarnation of the shader works on our famous smiley, but not my mesh. The normals are either being generated wrong or not being translated in the vertex shader correctly. I am over my head and can’t figure out where the problem is. I appreciate the help.

Also pardon the mix of gl_ and p3d_ prefixes

main.py - Sorry for the length of the code, but I included the cube sphere generator

from pandac.PandaModules import loadPrcFileData
loadPrcFileData('', 'frame-rate-meter-scale 0.035')
loadPrcFileData('', 'frame-rate-meter-side-margin 0.1')
loadPrcFileData('', 'show-frame-rate-meter 1')
loadPrcFileData('', 'window-title ' + "Planet Prototype")
loadPrcFileData('', "sync-video 0")
loadPrcFileData('', 'basic-shaders-only #f')
#loadPrcFileData('', 'dump-generated-shaders #t')


import direct.directbase.DirectStart
from panda3d.core import PointLight, Shader

### Planet Generator ###
from panda3d.core import Geom, GeomNode, GeomTriangles, GeomVertexData
from panda3d.core import GeomVertexFormat, GeomVertexWriter
from panda3d.core import NodePath, Shader, Vec3


def myNormalize(myVec):
    myVec.normalize()
    return myVec

format = GeomVertexFormat.getV3n3c4t2()
vdata = GeomVertexData('quadFace', format, Geom.UHDynamic)
vertex = GeomVertexWriter(vdata, 'vertex')
normal = GeomVertexWriter(vdata, 'normal')
color = GeomVertexWriter(vdata, 'color')
texcoord = GeomVertexWriter(vdata, 'texcoord')


def create_mesh(parentnp, debug=False):
    '''This creates a simple 17x17 grid mesh for the sides of our cube.
    The ultimate goal is to use a LOD system, probably based on quadtrees.
    If debug is true then we get a color gradiant on our vertexes.'''
    x = -1.0
    y = -1.0
    vertex_count = 0
    u = 0.0
    v = 0.0

    WIDTH_STEP = 2/16.0

    while y <= 1.0:
        while x <= 1.0:
            vertex.addData3f(x, y, 0)
            normal.addData3f(myNormalize((Vec3(2*x-1, 2*y-1, 2*0-1))))
            if debug:
                color.addData4f(1.0, u, v, 1.0)
            texcoord.addData2f(u, v)
            vertex_count += 1
            x += WIDTH_STEP
            u += WIDTH_STEP/2.0
        x = -1.0
        u = 0
        y += WIDTH_STEP
        v += WIDTH_STEP/2.0

    print vertex_count
    triangles = []

    for y in range(0, 16):
        for x in range(0, 16):
            v = 17 * y + x
            tri = GeomTriangles(Geom.UHDynamic)
            tri.addVertex(v)
            tri.addVertex(v+1)
            tri.addVertex(v+17)
            tri.closePrimitive()
            triangles.append(tri)

            tri = GeomTriangles(Geom.UHDynamic)
            tri.addVertex(v+1)
            tri.addVertex(v+18)
            tri.addVertex(v+17)
            
            tri.closePrimitive()
            triangles.append(tri)

    mesh = Geom(vdata)
    for t in triangles:
        mesh.addPrimitive(t)
    mnode = GeomNode('quadface')
    mnode.addGeom(mesh)
    nodePath = parentnp.attachNewNode(mnode)
    return nodePath


class Surface(object):
    '''planet is a parent nodepath that the 6 side mesh nodepaths will parent to.
    planet can be moved, scale, and rotated with no problems'''
    def __init__(self, name='planet', debug=False):
        '''Debug will generate colored pixles for fun time'''
        self.node_path = NodePath(name)
        self.sides = []
        for i in range(0, 6):
            m = create_mesh(self.node_path, debug)
            self.sides.append(m)

        '''The side meshes are rotated here. They are moved to their correct
        position in the shader'''
        self.sides[0].set_hpr(90, 90, 0)
        self.sides[1].set_hpr(-90, 90, 0)
        self.sides[2].set_hpr(0, 0, 0)
        self.sides[3].set_hpr(0, 180, 0)
        self.sides[4].set_hpr(0, 90, 0)
        self.sides[5].set_hpr(180, 90, 0)

    def set_pos(self, *args):
        self.node_path.set_pos(args)

    def set_scale(self, *args):
        self.node_path.set_scale(args)

    def reparent_to(self, *args):
        self.node_path.reparent_to(args)

    def set_texture(self, texture_path):
        '''Textures the surface. texture_path is a string in the same format as
        loader.loadCubeMap('texture_#.png') for a multifile cubemap. The orientation
        is a little different than what is described in the panda manual.

        North pole is z-up'''
        for i in range(0, 6):
            tex = loader.loadTexture(texture_path.replace('#', str(i)))
            self.sides[i].set_texture(tex)


def make_planet(name='planet', debug=False):
    return Surface(name, debug)

### END PLANET GENERATOR ###


planet = make_planet()
planet.reparent_to(render)

light = PointLight('light')
lightnp = render.attachNewNode(light)
lightnp.set_pos(0, -10, 0)
render.set_light(lightnp)

#axis = loader.loadModel('zup-axis')
#axis.reparent_to(render)

sphere = loader.loadModel('smiley')
sphere.set_pos(3, 0, 0)
sphere.reparent_to(render)
shaders = Shader.load(Shader.SLGLSL, 'phong_vert.glsl', 'phong_frag.glsl')
sphere.setShader(shaders)

shaders = Shader.load(Shader.SLGLSL, 'planet_surface_phong_vert.glsl', 'phong_frag.glsl')
planet.node_path.setShader(shaders)

def toggleWireframe():
    base.toggleWireframe()

base.accept('w', toggleWireframe)

run()

phong_vert.glsl

uniform mat4 p3d_ModelViewProjectionMatrix;
uniform mat4 p3d_ModelViewMatrix;
varying vec4 texCoords;

// Per pix lighting
//varying vec4 eye_position, eye_normal;
varying vec3 N;
varying vec3 v;
///Per pix lighting

in vec4 p3d_Vertex;
in vec4 p3d_Normal;

void main(void) {
    gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
    //eye_position = p3d_ModelViewMatrix * p3d_Vertex;
    //eye_normal.xyz = vec3(gl_NormalMatrix) * p3d_Normal.xyz;
    //eye_normal.w = 0.0;
    v = vec3(gl_ModelViewMatrix * gl_Vertex);       
    N = normalize(gl_NormalMatrix * gl_Normal);    
    texCoords = gl_MultiTexCoord0;          
}

planet_surface_phong_vert.glsl (adds the vertex normalizing code)

uniform mat4 p3d_ModelViewProjectionMatrix;
uniform mat4 p3d_ModelViewMatrix;
varying vec4 texCoords;

// Per pix lighting
//varying vec4 eye_position, eye_normal;
varying vec3 N;
varying vec3 v;
///Per pix lighting

in vec4 p3d_Vertex;
in vec4 p3d_Normal;
void main(void) {
    // Standard mode
    //gl_Position = p3d_ModelViewProjectionMatrix * gl_Vertex;

    vec4 vertex = p3d_Vertex;

    //By adding 1 to z we move our grid to make a cube.
    //We do 1 instead of 0.5 to make all the math easy.
    vertex.z = vertex.z + 1.0;

    //While this simple normalizing works it causes some distortion near the edges
    //While less distoration than a standard sphere there are more ideal solutions
    //Some cubemapping software will compensate for said distortions however, which is I use this
    gl_Position = p3d_ModelViewProjectionMatrix * vec4(normalize(vertex.xyz), vertex.w);
    
    //The normalization creates almost no edge distortion
    //Proof: http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html
    //float x = test.x;
    //float y = test.y;
    //float z = test.z;
    //test.x *= sqrt(1.0 - y * y * 0.5 - z * z * 0.5 + y * y * z * z / 3.0);
    //test.y *= sqrt(1.0 - z * z * 0.5 - x * x * 0.5 + z * z * x * x / 3.0);
    //test.z *= sqrt(1.0 - x * x * 0.5 - y * y * 0.5 + x * x * y * y / 3.0);
    //gl_Position = p3d_ModelViewProjectionMatrix * test;

    v = vec3(gl_ModelViewMatrix * vertex);       
    N = normalize(gl_NormalMatrix * gl_Normal);    
    texCoords = gl_MultiTexCoord0;                
}

phong_frag.glsl

uniform sampler2D p3d_Texture0;
varying vec2 texture_coordinate;
varying vec4 texCoords;
//varying vec4 eye_position, eye_normal;

varying vec3 N;
varying vec3 v;    

uniform mat4 plight_plight0_rel_view;

//varying out vec4 FragColor;

void main(void)
{
   vec3 L = normalize(gl_LightSource[0].position.xyz - v);   
   vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0)  
   vec3 R = normalize(-reflect(L,N));  
 
   //calculate Ambient Term:  
   vec4 Iamb = gl_FrontLightProduct[0].ambient;    

   //calculate Diffuse Term:  
   vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);
   Idiff = clamp(Idiff, 0.0, 1.0);     
   
   // calculate Specular Term:
   vec4 Ispec = gl_FrontLightProduct[0].specular 
                * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess);
   Ispec = clamp(Ispec, 0.0, 1.0); 

   // write Total Color:  
   gl_FragColor = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec;  
}

Need ‘planet_surface_vert.glsl’, ‘planet_surface_frag.glsl’

Oh you didn’t need those shaders.
Fixed by just

        shaders = Shader.load(Shader.SLGLSL, 'planet_surface_vert.glsl', 'planet_surface_frag.glsl')
        self.node_path.setShader(shaders)

in the Surface class on lines 95 and 96. My post is updated as well

try

N = normalize(gl_NormalMatrix * vertex.xyz);

instead of

N = normalize(gl_NormalMatrix * gl_Normal);

Ninth. I could kiss you. Not the make out kind, but the light fluttery on the eyelids type.