Moving spot light

Started by iblis, January 19, 2013, 10:59:55 PM

Previous topic - Next topic

iblis

I need to create spot light (It will be car's light). I want it to look like here http://www.youtube.com/watch?v=7Xx_Rk6wXqU .
I think I should calculate the modelViewMatrix for light (light moves with carBody, so it would be the same matrix), save it as lightMatrix0 and transform light position in each vertex transformation.. but it doesn't work. I've found here http://www.jpct.net/forum2/index.php/topic,2368.0.html how calculate model view matrix:
mo.setTo(transBuffer);
mat.setTo(cam.getBack());
mat.transformToGL();
mo.translate(-cam.backBx, -cam.backBy, -cam.backBz);
mo.matMul(mat);

Is it correct? Maybe I do sth wrong?

Vertex Shader:
uniform mat4 modelViewMatrix;
uniform mat4 modelViewProjectionMatrix;

uniform mat4 lightMatrix0; //in my opinion here should be light's model view matrix
uniform vec3 lightPos[2];
uniform vec3 lightDir[2];

attribute vec4 position;
attribute vec3 normal;

varying vec3 eyeMV;
varying vec3 normalMV;
//I want to use 2 lights, but one is easier to test
varying vec3 lightPosMV[2];
varying vec3 lightDirMV[2];

void main(void)
{
normalMV = normalize(modelViewMatrix * vec4(normal, 0.0)).xyz;

vec3 vertex = (modelViewMatrix * position).xyz;
eyeMV = -vertex;

mat4 lightMatrix[2];
lightMatrix[0] = lightMatrix0;

for(int i = 0; i < 1; i++){
    lightDirMV[i] = normalize(lightMatrix[i]* vec4(lightDir[i], 0.0)).xyz;
    vec3 light =normalize( lightMatrix[i] * vec4(lightPos[i], 0.0)).xyz;
    lightPosMV[i] = normalize(light - vertex);
}
gl_Position = modelViewProjectionMatrix * position;
}

Fragment Shader

precision mediump float;

uniform vec3 lightDir[2];
varying vec3 eyeMV;
varying vec3 normalMV;
varying vec3 lightPosMV[2];
varying vec3 lightDirMV[2];

const float outerSpot = 0.6;
const float specExp = 64.0;
const vec3 ambient = vec3(0.3, 0.3, 0.3);
const vec3 diffuse = vec3(0.7, 0.7, 0.7);
const vec3 specular = vec3(1.0, 1.0, 1.0);

void main(void)
{
    vec4 color = vec4(ambient, 1.0);
    vec3 eyeN = normalize(eyeMV);
    vec3 normalN = normalize(normalMV);

    for(int i = 0; i < 1; i++){
    vec3 lightPosN = normalize(lightPosMV[i]);
    vec3 lightDirN = normalize(lightDirMV[i]);
    float diff = dot(normalN, lightPosN);

    if(diff >= 0.0){
        float spot = dot(-lightPosN, lightDirN);
        if(spot > outerSpot){
            color += vec4(diff * diffuse, 0.0);
            vec3 ref = normalize(reflect(-lightPosN, normalN));
            float spec = max(0.0, dot(eyeN, ref));
            float buf = pow(spec, specExp);
            color +=  vec4(specular * buf, 0.0);
        }
    }
    }
    gl_FragColor =clamp(color, 0.0, 1.0);
}

Java
Matrix light2[] = new Matrix[]{
new Matrix(), new Matrix()
};

Matrix matrixBuf = new Matrix();

private void setUpLights(Car cars[]) {
GLSLShader phong = this.shaders.get("phong");
this.world.setGlobalShader(phong);

Camera camera = world.getCamera();
camera.getPosition(buf);
for (int i = 0; i < 1; i++) {
// I've relied on http://www.jpct.net/forum2/index.php/topic,2368.0.html Is it really create the model view matrix correctly?

cars[i].carBody.object3d.getWorldTransformation(matrixBuf);
light2[i].setTo(matrixBuf);
mat.setTo(camera.getBack());
mat.transformToGL();

light2[i].translate(-buf.x, -buf.y, -buf.z);
light2[i].matMul(mat);

phong.setUniform("lightMatrix"+i, light2[i]);
}
phong.setUniform("lightPos", new SimpleVector[]
{ SimpleVector.create(0.0f, 0.5f, 0.0f), SimpleVector.create(0.0f, 0.5f, 0.0f)});
phong.setUniform("lightDir", new SimpleVector[]
{ SimpleVector.create(0.0f, 0.0f, -1.0f), SimpleVector.create(0.0f, 0.0f, -1.0f)});

}

Thomas.

Model view matrix in shader is not needed, you should calculate direction one time when camera or light direction is changed. You can inspire by this example of spot light shader. But take into account per-pixel lighting on mobile devices is not very fast. I am using it in my game, but with 8 spot lights it is running only about 18fps on SGS3 (without post-processing effects).

iblis

I just need 2-3 lights.
Could you explain me how to calculate camera direction. I can't multiply camera's position and direction by modelViewMatrix because it contains object's transformation.

Thomas.

If you do it by this way, your game will be only for high-end devices. Anyway you need direction in camera space, by code below you can get it. Probably will be faster if you project "light texture" on the ground. It uses many games, for example GTA...

public static void vectorWorldToCameraSpace(World world, SimpleVector vector, SimpleVector toFill) {
Camera cam = world.getCamera();
toFill.set(vector);
Matrix back = cam.getBack();
toFill.matMul(back);
}

iblis

 Sth is wrong all the time. The problem is connected with the position of the light (or vector to the light).

Java

public void vectorWorldToCameraSpace(SimpleVector vector,
SimpleVector toFill) {

Camera cam = world.getCamera();
toFill.set(vector);
Matrix back = cam.getBack();
toFill.matMul(back);
}

private void setUpLights() {
GLSLShader phong = this.shaders.get("phong");
this.world.setGlobalShader(phong);

SimpleVector dir[] = new SimpleVector[] {
SimpleVector.create(0.0f, 0.0f, 1.0f),
SimpleVector.create(0.0f, 0.0f, 1.0f) };

for (int i = 0; i < 2; i++) {

box.getTransformedCenter(pos[i]); //So light should be in the center of box (0, 0, 0)
vectorWorldToCameraSpace(pos[i], posBuf[i]);
vectorWorldToCameraSpace(dir[i], dirBuf[i]);
}
phong.setUniform("lightPos", posBuf);
phong.setUniform("lightDir", dirBuf);

}

Vertex Shader
uniform mat4 modelViewMatrix;
uniform mat4 modelViewProjectionMatrix;


uniform vec3 lightPos[2];
uniform vec3 lightDir[2];

attribute vec4 position;
attribute vec3 normal;

varying vec3 eyeMV;
varying vec3 normalMV;
//I want to use 2 lights, but one is easier to test
varying vec3 lightPosMV[2];
varying vec3 lightDirMV[2];

void main(void)
{
normalMV = normalize(modelViewMatrix * vec4(normal, 0.0)).xyz;

vec3 vertex = (modelViewMatrix * position).xyz;
eyeMV = -vertex;

for(int i = 0; i < 2; i++){
    lightDirMV[i] = normalize(lightDir[i]);
    vec3 light = lightPos[i];
    lightPosMV[i] = normalize(light - vertex);
}
gl_Position = modelViewProjectionMatrix * position;
}

Fragment Shader
precision mediump float;

varying vec3 normalMV;
varying vec3 lightPosMV[2];
varying vec3 lightDirMV[2];

const float outerSpot = 0.6;
const vec3 ambient = vec3(0.2, 0.2, 0.2);
const vec3 diffuse = vec3(0.7, 0.7, 0.7);

void main(void)
{
    vec4 color = vec4(ambient, 1.0);
    vec3 normalN = normalize(normalMV);

    for(int i = 0; i < 1; i++){
    vec3 lightPosN = normalize(lightPosMV[i]);
    vec3 lightDirN = normalize(lightDirMV[i]);
    float diff = dot(normalN, lightPosN);

    if(diff >= 0.0){
        float spot = dot(-lightPosN, lightDirN);
        if(spot > outerSpot){
            color += vec4(diff * diffuse, 0.0);
        }
    }
    }
    gl_FragColor =clamp(color, 0.0, 1.0);
}


Thomas.

Yes, position is wrong, only light direction should by in camera space... change "lightPos[2]" to "lightPositions[8]". And loops is not good to use in the mobile shaders. Better way is to use "if(lightCount >= 0) { ... if(lightCount >= 1){...}}" or generate the specific shader.

iblis

"lightPositions[8]"? Should I use Light class and set position of lights like this?

this.carBody.object3d.getTransformedCenter(position);
light.setPosition(position);

Does the Light class set "lightPositions[8]"?
This a result.

The light rotates with the camera ;/ But it should rotate with the car. Moreover the car moves right, but the light moves left. I think it works the same earlier.
Vertex Shader:

uniform mat4 modelViewMatrix;
uniform mat4 modelViewProjectionMatrix;
uniform vec3 lightPositions[8];


uniform vec3 lightDir[2];

attribute vec4 position;
attribute vec3 normal;

varying vec3 eyeMV;
varying vec3 normalMV;
//I want to use 2 lights, but one is easier to test
varying vec3 lightPosMV[2];
varying vec3 lightDirMV[2];

void main(void)
{
normalMV = normalize(modelViewMatrix * vec4(normal, 0.0)).xyz;

vec3 vertex = (modelViewMatrix * position).xyz;
eyeMV = -vertex;


lightDirMV[0] = normalize(lightDir[0]);
lightPosMV[0] = normalize(lightPositions[0] - vertex);

gl_Position = modelViewProjectionMatrix * position;
}

Thomas.

Axises of light direction is probably inverted. Try this:

dirBuf[i].y *= -1;
dirBuf[i].z *= -1;

iblis

The direction is correct :) JPCT coordinates != OpenGL coordinates :)
But sth is wrong with position.

car.carBody.object3d.getTransformedCenter(position);
light.setPosition(position);

The position of the red box is 0, 0, 0.

The car (bigger red box) moved  to 0, 0, -20. The light changed x and z coordinates.