Shader based on distance from lightsources

Started by tollos, January 08, 2013, 11:11:27 AM

Previous topic - Next topic

tollos

Hello,

Currently my game don't have any lightning (except the ambiant one) and I would like to add some.
Problem is that it's block-based, not a single loaded object, and due to the vertex based lightning, it looks horrible when I try.

I tried to use shaders but being totally new with them it's a bit difficult to get something nice ;-)

What I need is shaders that works on multiple light sources (maybe even colored but that's not mandatory) and based on distance but not on normals ... and that don't go trough walls if possible.

Does someone have a shader that could fit that need ?

Thanks in advance !

EgonOlsen

I guess your problem are the transitions between the tiles where the lighting looks wrong? Can you post an example of how your tiles look like? Maybe there are other ways to solve this.

tollos

You can find the screenshot bellow, the ground here is made of some box, should have the same shading but you can clearly see the difference.

Maybe changing the normal of all cubes could help it but no idea how to do it ;-)

Also another little question about lights ... if I put a light high like a sun and then render an interior scene, the light is still applied inside the building, does that means there are no collision test for light rays ?

BTW ... the 8-bit retro style is what I want ;-)

Thanks for your help !


EgonOlsen

I made a quick demo to show one way to change normals. It sets the normals of all vertices to the same normal...while this improves the lighting in this little example, it might not be suitable for your game to do it this simple way, but i think you'll get the idea. Another option is to create the normals in a modeller, save the result in OBJ-format and set http://www.jpct.net/doc/com/threed/jpct/Config.html#useNormalsFromOBJ to true before loading it.

If you start this program, it will render a 20*20 field of cubes with one light and the usual issues. Removing the comment from the call to changeNormals(...) will modify the normals and improve the lighting. It's still not 100% perfect, but it don't think that this will be noticable in an actual game with textures and such.

About the "collision for light rays" thing...no, this isn't the way lighting in OpenGL (or any low level 3d api) works. This kind of lighting happens per polygon, it doesn't take any other part of the scene into account. What you mean with "collision for light rays" is usually called "shadows"... ;). jPCT has a ShadowHelper for simple shadow mapping, but i don't think that the results will fit the style of your game.


import org.lwjgl.opengl.Display;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.GenericVertexController;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Lights;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;
import com.threed.jpct.util.Light;

public class CubeWorld {

public static void main(String[] args) throws Exception {
FrameBuffer buffer = new FrameBuffer(1024, 768, FrameBuffer.SAMPLINGMODE_GL_AA_4X);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);

World world = new World();
world.setAmbientLight(0, 0, 0);
world.getLights().setRGBScale(Lights.RGB_SCALE_2X);

Light light = new Light(world);
light.setIntensity(255, 0, 0);
light.setPosition(new SimpleVector(0, 0, 180));
light.setAttenuation(20);

Object3D cube = Primitives.getCube(5);
cube.rotateY((float) -Math.PI / 4f);
cube.rotateMesh();
cube.clearRotation();

cube.build();

// uncomment this to set new, unified normals
// changeNormals(cube, new SimpleVector(0, 0, -1));

cube.compile();

for (int x = 0; x < 20; x++) {
for (int y = 0; y < 20; y++) {
Object3D tmp = new Object3D(cube, true);
tmp.translate((x - 10) * 5, (y - 10) * 5, 200);
tmp.build();
tmp.compile();
tmp.shareCompiledData(cube);
world.addObject(tmp);
}
}

while (!Display.isCloseRequested()) {
buffer.clear();
world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
Thread.sleep(10);
}
}

private static void changeNormals(Object3D obj, final SimpleVector newNormal) {
obj.getMesh().setVertexController(new GenericVertexController() {
private static final long serialVersionUID = 1L;

@Override
public void apply() {
SimpleVector[] dest = this.getDestinationNormals();
for (SimpleVector sv : dest) {
sv.set(newNormal);
}
}
}, true);
obj.getMesh().applyVertexController();
obj.getMesh().removeVertexController();
}
}


tollos

Thanks for your help !

I tried it but even if it's good for exteriors, it's not good for interiors. I think I will stick with "no lights" policy for now ;-)


EgonOlsen

Quote from: tollos on January 09, 2013, 11:56:32 AM
I tried it but even if it's good for exteriors, it's not good for interiors. I think I will stick with "no lights" policy for now ;-)
That's what you could fight by doing this:

QuoteAnother option is to create the normals in a modeller, save the result in OBJ-format and set http://www.jpct.net/doc/com/threed/jpct/Config.html#useNormalsFromOBJ to true before loading it.

tollos

Yes I know, but nothing is generated with a modeler (Never used one before) , everything is done by code.

Finally I managed to understand the shaders and did one not based on normals but only affected by the distance to the light sources but ... still not okay since shaders are working by vertex to compute the distance to the light, I still have strange effects on texture.

Anyway, thanks for your quick support ;-)

By the way is someone is interested, this is the shader I made to test, only lightsources diffuse value affects the lightning and no normals are used.

Vertex Shader:

varying vec3 lightDir;
varying float att[gl_MaxLights];

void main()
{
vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex);
int i;
for (i=0; i<gl_MaxLights; ++i) {
lightDir = vec3(gl_LightSource[i].position.xyz - vVertex);
float d = length(lightDir);

att[i] = 1.0 / ( gl_LightSource[0].constantAttenuation +(gl_LightSource[0].linearAttenuation*d) +(gl_LightSource[0].quadraticAttenuation*d*d) );
}

gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}


Pixel Shader:

varying float att[gl_MaxLights];
uniform sampler2D colorMap;

void main (void)
{
vec4 final_color;
int i;
for (i=0; i<gl_MaxLights; ++i) {
if(att[i]>0) {
final_color = final_color + ( gl_LightSource[i].diffuse * att[i] * texture2D( colorMap, gl_TexCoord[0].st ) ) ;
}
}

gl_FragColor = final_color;
}


EgonOlsen

Have you tried to make the distance a varying instead of the actual light intensity?

tollos

#8
Okay tried it and it looks way better now, back to lightning !   :o

Not perfect yet ... and won't be since I ignore the normals ... but it's already very nice for interriors.

Again, many thanks for your quick answer ;-)