Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Super-basic lighting shader: What is wrong?

edited February 2014 in Shaders Posts: 8

Okay, so I am a bit puzzled. I am trying to write a super-basic lighting shader, and everything seems to go great until I go and try to multiply the texture color by the dot product of the light and normal vectors. Then, the cube doesn't render at all.

Here is my vertex shader:


uniform mat4 modelViewProjection;

attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec3 normals;

uniform mat4 mModel;
uniform mat4 mView;
uniform mat4 mProjection;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying highp vec4 vNormal;

void main()
{
    vColor = color;
    vTexCoord = texCoord;
    vNormal = normalize(vec4(normals,0.0)*mModel);
    gl_Position = modelViewProjection * position;
}

The fragment shader:


precision highp float;

uniform lowp sampler2D texture;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying highp vec4 vNormal;

void main()
{
    highp vec4 lightDir = normalize(vec4(1,1,-1,0));
    highp float lit = dot(lightDir, vNormal);

    lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
    
    //Commenting out the above, and uncommenting the line below,
    //the shader does not work. There is not even a black cube, just nothing
    
    //lowp vec4 col = texture2D( texture, vTexCoord ) * vColor*lit;

    gl_FragColor = col;
    
}

Here is the code for the actual block I try to render:

 
Block = class()
 
function Block:init()
    -- you can accept and set parameters here
 
    local vertices = {
      vec3(-0.5, -0.5,  0.5), -- Left  bottom front
      vec3( 0.5, -0.5,  0.5), -- Right bottom front
      vec3( 0.5,  0.5,  0.5), -- Right top    front
      vec3(-0.5,  0.5,  0.5), -- Left  top    front
      vec3(-0.5, -0.5, -0.5), -- Left  bottom back
      vec3( 0.5, -0.5, -0.5), -- Right bottom back
      vec3( 0.5,  0.5, -0.5), -- Right top    back
      vec3(-0.5,  0.5, -0.5), -- Left  top    back
    }
 
 
    local normals = {
                    vec3(-1,-1,1):normalize(),
                    vec3(1,-1,1):normalize(),
                    vec3(1,1,1):normalize(),
                    vec3(-1,1,1):normalize(),
                    vec3(-1,-1,-1):normalize(),
                    vec3(1,-1,-1):normalize(),
                    vec3(1,1,-1):normalize(),
                    vec3(-1,1,-1):normalize()
                    }
 
    local triangles = {
                    1,2,3,
                    1,3,4,
                    2,6,7,
                    2,7,3,
                    6,5,8,
                    6,8,7,
                    5,1,4,
                    5,4,8,
                    4,3,7,
                    4,7,8,
                    5,6,2,
                    5,2,1
                    }
 
    local cubeVerts = {}
    for i,v in ipairs(triangles) do
        table.insert(cubeVerts,vertices[v])
    end
 
 
    local normalVecs = {}
    for i,v in ipairs(triangles) do
        table.insert(normalVecs,normals[v])
    end
        -- all the unique texture positions needed
    local texvertices = { vec2(0.03,0.24),
                          vec2(0.97,0.24),
                          vec2(0.03,0.69),
                          vec2(0.97,0.69) }
 
    -- apply the texture coordinates to each triangle
    local triTexCoords =   {1,2,4,
                            1,4,3,
 
                            1,2,4,
                            1,4,3,
 
                            1,2,4,
                            1,4,3,
 
                            1,2,4,
                            1,4,3,
                            1,2,4,
                            1,4,3,
 
                            1,2,4,
                            1,4,3
                            }
 
    cubetexCoords = {}
    for i,v in ipairs(triTexCoords) do
        table.insert(cubetexCoords,texvertices[v])
    end
 
    self.cube = mesh()
 
    self.cube.texture = "Planet Cute:Dirt Block"
 
    self.cube.vertices = cubeVerts
    self.cube.texCoords = cubetexCoords
    self.cube.normals = normalVecs
    self.cube.shader=shader("Documents:LightingTest")
    self.cube.shader.mMatrix = modelMatrix()
    self.cube.shader.mView  = viewMatrix()
    self.cube.shader.mProjection = projectionMatrix()
     self.cube:setColors(255,255,255,255)
 
end
 
 
 
function Block:draw()
    -- Codea does not automatically call this method
 
    self.cube:draw()
 
end
 
function Block:touched(touch)
    -- Codea does not automatically call this method
end

And finally, my Main:

 
-- 3D basic
 
-- Use this function to perform your initial setup
function setup()
 
    local dirtBlock = Block()
    local grassBlock = Block()
    grassBlock.cube.texture = "Planet Cute:Grass Block"
 
    geometry={dirtBlock, grassBlock}
 
    t=0
   -- currentScene = CubeDemo()
    scene = { { {1} } }  
 
end
 
-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    perspective()
    camera(0,500,300, 0,0,0, 0,0,1)
 
    rotate(40*t)
 
    scale(101)
 
    for iz,zv in ipairs(scene) do
        for iy,yv in ipairs(zv) do
            for ix, xv in ipairs(yv) do
                pushMatrix()
 
                translate(ix-1,iy-1,-iz+1)
 
                if xv > 0 then
                    geometry[xv]:draw()
                end
                popMatrix()
            end
        end
    end
 
    t = t + DeltaTime
 
end

Comments

  • Posts: 2,161

    Try

    highp float lit = .5 + .5 * dot(lightDir,vNormal);
    
  • Is there anything @andrew_stacey DOESNT have the solution for?!?!
    Andrew...are you...are you god?

  • IgnatzIgnatz Mod
    edited February 2014 Posts: 5,396

    @mooglinux - A couple of things (they don't cause the problem you are seeing, though). You don't need to set viewMatrix and projectionMatrix for the shader, and the line that sets mMatrix should be in Block:draw, just before the block is drawn (so it is updated at every draw).

  • Jmv38Jmv38 Mod
    Posts: 3,295

    @monkeyman lo! no Andrew is not God. And although God has never seen Andrew, he belives in Andrew's existence and allmighty wisdom anyway.

  • IgnatzIgnatz Mod
    edited February 2014 Posts: 5,396

    @mooglinux - I think the main problem is you named the model matrix mModel in the shader, and mMatrix in the cube code. To fix it, put this line at the top of Block:draw (as suggested in my previous message)

    self.cube.shader.mModel=modelMatrix()
    

    Additionally, when calculating normals in the vertex shader, you need to put the matrix first, ie

    vNormal= mModel * vec4(normal,0.0);
    

    Note you use the name normal (singular) in the vertex shader, not normals (plural). This is because Codea uses this name for individual normal values.

    Additionally, because the fragment shader will interpolate the normal, it is a good idea to normalize vNormal again within the fragment shader before using it (which means there is no point normalizing vNormal, so I have not done so, above). It won't make a difference in this case, where all the interpolated values within a triangle are the same, but I think it's worth changing anyway.

  • IT WORKS! Thanks! Mistyping things seems to have been the main problem heh.

    Here is the working version:

    Vertex Shader:

    uniform mat4 modelViewProjection;
    
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 texCoord;
    attribute vec3 normal;
    
    uniform mat4 mModel;
    uniform mat4 mView;
    uniform mat4 mProjection;
    
    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;
    varying highp vec4 vNormal;
    
    void main()
    {
        vColor = color;
        vTexCoord = texCoord;
        vNormal = mView*vec4(normal,0.);
        gl_Position = modelViewProjection * position;
    }
    

    And fragment shader:

    precision highp float;
    
    uniform lowp sampler2D texture;
    
    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;
    varying highp vec4 vNormal;
    
    void main()
    {
        highp vec4 lightDir = normalize(vec4(1.,-1.,1.,0.));
        highp float lit = .5+.5*dot(lightDir, normalize(vNormal));
        
        //lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
        lowp vec4 col = texture2D( texture, vTexCoord ) * vColor*lit;
        col.a=1.;
        gl_FragColor = col;
    
        
    }
    
    

    Now, one peculiar thing, is that the light source never moves in relation to the cube as the cube rotates. Since the light vector is defined in the fragment shader, I would have expected that vector to be in screen-space, and so stay put. Why isn't that the case?

  • IgnatzIgnatz Mod
    Posts: 5,396

    @mooglinux - just looking at how you calculate normals, you seem to be defining them for each corner, since you have 8, whereas you should only have 6, one for each face.

    The difference is that either
    (a) each corner always has the same normal whenever it is used as a vertex, or
    (b) the normal for each vertex depends on which face it is used for, so if a corner gets used 3 times for different vertices, it will have 3 different normals.

    (b) is correct. So for example, the first six vertices are for the front face, so you should have

    for i=1,6 do
        normalVecs[i]=vec3(0,0,1)
    end
    
  • Posts: 2,161

    The location of the light source is not determined by where you calculate the proportion of light falling, but by what vectors you use to compute it. Since you multiply the normals by just the view matrix, then it's actually a bit confused. You explicitly don't transform the normals from the model space to the view space. This will have the effect of making it seem as though the light is fixed relative to the cube.

    As well as using the wrong matrix, you apply it incorrectly. To transform the normal vectors, multiply by the inverse of the transpose. (I think I explained this in a post somewhere here, so I'll not repeat myself now.). Sometimes it's the same thing, but not always.

    There are also a few optimisations that can be done in your shader. If your faces are flat, you can compute the light factor in the vertex shader and assign it to a varying. Also, you can compute values such as lightdir outside main to avoid computing them again for every pixel.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Andrew_Stacey, @mooglinux - my understanding is that unless you rescale the axes differently, ie distort the mesh, then using modelMatrix is fine. I have done that for all my lighting shaders.

    modelMatrix converts the normals to world space, which is where the lighting is defined, so that is (I believe) consistent. Again, that is the approach I use.

    So I'm not sure modelMatrix has anything to do with the main problem, which may be that the normals are incorrectly defined.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @mooglinux - I have it working for you here, I believe

    https://gist.github.com/dermotbalson/9246685

    The problem was the normals. Look at how I've defined them. I've also changed the way you filled the vertex and texture table. Using "for i,v in pairs" does not necessarily give you the items in the order you put them in the table, so to be safe use "for i=1,#table" to ensure correct ordering.

    I've made a couple of small changes to the shader, including normalising the normal in the fragment shader (yes, you can do it more efficiently in the vertex shader, but you are just experimenting at the moment, so what you have is fine). I also set the alpha of the resulting pixel color to 1, as you don't want transparent pixels with lighting.

    You'll also see I've embedded the shader code at the bottom of the other code. It simply goes into two text chunks within a little table.

    If you aren't familiar with any of this or want more info (even if it's just on Codea-specific stuff), I suggest reading the ebooks I've written on shaders and also on lighting, which you can find at the top of this link

    http://coolcodea.wordpress.com/2013/06/19/index-of-posts/

Sign In or Register to comment.