Need help regarding setup of Thomas' shadow implementation in my example.

Started by Wolf17, February 27, 2015, 06:33:45 AM

Previous topic - Next topic

Wolf17


Means like this?

public static void renderWithShader(World world, FrameBuffer fb, GLSLShader shader) {
Enumeration<Object3D> objects = world.getObjects();
while (objects.hasMoreElements()) {
Object3D obj = (Object3D) objects.nextElement();
if (obj.getVisibility()) {
GLSLShader objShader = obj.getShader();
shaders.add(objShader);
obj.setShader(shader);
}
}

world.renderScene(fb);
world.draw(fb);
objects = world.getObjects();
int i = 0;
while (objects.hasMoreElements()) {
Object3D obj = (Object3D) objects.nextElement();
if (obj.getVisibility()) {
GLSLShader objShader = shaders.get(i);
obj.setShader(objShader);
i++;
}
}
shaders.clear();
}


Or do I have to do anything else too? This snippet is called when I want to assign depthshader to the object3ds.

EgonOlsen

No, that assigns the depthmap shader. Shadow mapping requires multiple actions. I don't know about Thomas. actual implemention because i haven't checked it out in detail, but it goes something like this:


  • Assign the depthmap as a second texture layer to all objects that should receive shadows (*)
  • Asssign depthmap-shader to all objects that should cast shadows (*)
  • Setup the camera, so that the world can be rendered from the projector's point of view (*)
  • Render these objects only, which results in the depth map (*)
  • Reset the view to the normal point of view (*)
  • Assign a shader to all objects that should receive shadows that takes the depthmap, projects it onto the scene using the projection matrix from the projector and renders the scene with shadows (**)
  • ...repeat...

(*) = You seem to be doing this (not sure if correctly, but there is code that seems to handle this)
(**) = I'm missing that part. All i can see is that you load the shaders from the zip and assign them to the ShaderProvider (whatever that does...). But i don't see that you are actually using them on your objects. If your normal objects are using a custom shader already, you should modify it in a way that it does the shadow mapping as well. If not, you might be able to pick one from the set in the zip, but i'm not sure, which...

Maybe i should finally create a little test case that demonstrates basic shadow mapping on Android... :-\

EgonOlsen

Ok, here you go: http://jpct.de/download/example/ShadowExample.zip

And you'll need a slightly updated version of jPCT-AE for this to work correctly in portrait mode. You can grab this here: http://jpct.de/download/beta/jpct_ae.jar

It's based on Thomas.' work but simplified to one ShadowHelper class (a little similar to what desktop jPCT already offers). That's not saying that Thomas.' source code is too complicated, but it has a much larger scope than this small example, so i decided to cut some stuff and modify some parts. Anyway, without his great work, i couldn't have done this in just 2 hours.

Image:


I hope this helps somehow. This example uses a single shader for all receiver objects. It's based on the most complex default shader that jPCT-AE offers. So performance might not be optimal in all cases. In addition, it assumes that the depthmap is in texture unit 1, which might not be the case for multitextured objects. Anyway. it's just an example to get you started. I hope it helps.

If i find the time, i'll add it to the wiki in some modified form and if i find even more time, i'll add an even more modified version to the actual release... ;)


Wolf17

    Thank You EgonOlsen!! For posting exaple  And also for explaining the basic shadow mapping process.****You really help a lOT!  :)******
I'll try this ! :)

maggie

Quote from: EgonOlsen on February 27, 2015, 11:11:29 PM
Ok, here you go: http://jpct.de/download/example/ShadowExample.zip

And you'll need a slightly updated version of jPCT-AE for this to work correctly in portrait mode. You can grab this here: http://jpct.de/download/beta/jpct_ae.jar

It's based on Thomas.' work but simplified to one ShadowHelper class (a little similar to what desktop jPCT already offers). That's not saying that Thomas.' source code is too complicated, but it has a much larger scope than this small example, so i decided to cut some stuff and modify some parts. Anyway, without his great work, i couldn't have done this in just 2 hours.

Image:


I hope this helps somehow. This example uses a single shader for all receiver objects. It's based on the most complex default shader that jPCT-AE offers. So performance might not be optimal in all cases. In addition, it assumes that the depthmap is in texture unit 1, which might not be the case for multitextured objects. Anyway. it's just an example to get you started. I hope it helps.

If i find the time, i'll add it to the wiki in some modified form and if i find even more time, i'll add an even more modified version to the actual release... ;)

Based on this example, I'm trying to have a transparent receiver by simply adding: plane.setTransparency(20)
And here is the result:


Is it possible to have a dark color shadow with a transparent receiver? Could you please give me a hint how to do that in jpct-ae?
Basically what I want to achieve is exactly the same with this post: http://www.jpct.net/forum2/index.php/topic,2626.msg19433.html. The only different thing is I'm using jpct-ae.

EgonOlsen

Might be the shader in that particular example. You might want to try the latest beta instead, which comes with a more advanced shadow support in form of the new ShadowHelper class for AE: http://jpct.de/download/beta/jpct_ae.jar

maggie

The shadow still makes the receiver looks like have a "hole"... :(
Plus, the shadow is not mapped correctly. Probably because of my Projector, not so sure if I set all needed parameters correctly.
Here is my code:


public void onSurfaceChanged(GL10 gl, int w, int h) {
try {
Config.farPlane = 5000;
buffer = new FrameBuffer(w, h);

world = new World();
world.setAmbientLight(100, 100, 100);

TextureManager tm = TextureManager.getInstance();
tm.addTexture("floor", new Texture(ShadowExample.this.getBaseContext().getAssets().open("floor.jpg")));

myObject = Object3D.mergeAll(Loader.loadOBJ(
getApplicationContext().getAssets().open("teapot.obj"), null, 10));
myObject.build();
myObject.translate(5, 10, -10);
world.addObject(myObject);

plane = ExtendedPrimitives.createPlane(100, 2);
plane.setTexture("floor");
plane.setTransparency(20);
plane.build();
plane.translate(0, 20, 10);
world.addObject(plane);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 50);
cam.lookAt(plane.getTransformedCenter());

SimpleVector sv = new SimpleVector(myObject.getTransformedCenter());
sv.y -= 70;
sv.z = 30;

                                sun = new Light(world);
sun.setIntensity(250, 250, 250);
sun.setPosition(sv);

Projector projector = new Projector();
projector.setFOVLimits(0, 999);
float fov = projector.convertDEGAngleIntoFOV(90);
projector.setFOV(fov);
projector.setYFOV(fov);
projector.setPosition(myObject.getTransformedCenter());

sh = new ShadowHelper(buffer, projector, 512);
sh.setLightSource(projector);
sh.addCaster(myObject);
sh.addReceiver(plane);

MemoryHelper.compact();
} catch (Exception e) {
throw new RuntimeException(e);
}
}




public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
myObject.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
myObject.rotateX(touchTurnUp);
touchTurnUp = 0;
}

sh.updateShadowMap(buffer, world);

buffer.clear(back);

world.renderScene(buffer);
world.draw(buffer);

buffer.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
}

EgonOlsen

Why is your projector inside of your object? Is that intentional? Apart from that, I'm not sure what the issue should be. I made myself a test case, set the transparency to 20 and the shadows looked just fine. Are you sure that your code uses the ShadowHelper from the jar and not the one from the zip?

maggie

Sorry, it was a mistake. Just ignore that line.
Yup, I'm sure that I use ShadowHelper from the jar  :(

EgonOlsen

Works fine for me...do you have complete test case for me to try?

maggie

Here is the full code:


import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Loader;
import com.threed.jpct.Logger;
import com.threed.jpct.Object3D;
import com.threed.jpct.Projector;
import com.threed.jpct.RGBColor;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.ExtendedPrimitives;
import com.threed.jpct.util.MemoryHelper;
import com.threed.jpct.util.ShadowHelper;

public class ShadowExample extends Activity {

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer buffer = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);

private float touchTurn = 0;
private float touchTurnUp = 0;

private float xpos = -1;
private float ypos = -1;

private Object3D myObject = null;
private Object3D plane = null;
private int fps = 0;

private Light sun = null;
private ShadowHelper sh;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());
mGLView.setEGLContextClientVersion(2);
renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);
}

@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}

@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}

@Override
protected void onStop() {
super.onStop();
System.exit(0);
}

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}

if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;

xpos = me.getX();
ypos = me.getY();

touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}

try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}

return super.onTouchEvent(me);
}

protected boolean isFullscreenOpaque() {
return true;
}

class MyRenderer implements GLSurfaceView.Renderer {

private long time = System.currentTimeMillis();

public MyRenderer() {
}

public void onSurfaceChanged(GL10 gl, int w, int h) {
try {
Config.farPlane = 5000;
buffer = new FrameBuffer(w, h);

world = new World();
world.setAmbientLight(100, 100, 100);

TextureManager tm = TextureManager.getInstance();
tm.addTexture("floor", new Texture(ShadowExample.this.getBaseContext().getAssets().open("floor.jpg")));

myObject = Object3D.mergeAll(Loader.loadOBJ(
getApplicationContext().getAssets().open("teapot.obj"), null, 12));
myObject.build();
myObject.translate(0, 10, 0);
world.addObject(myObject);

plane = ExtendedPrimitives.createPlane(100, 2);
plane.setTexture("floor");
plane.setTransparency(20);
plane.build();
plane.translate(0, 40, 10);
world.addObject(plane);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 50);
cam.lookAt(plane.getTransformedCenter());

SimpleVector sv = new SimpleVector(myObject.getTransformedCenter());
sv.y -= 100;
sv.z += 30;

sun = new Light(world);
sun.setIntensity(250, 250, 250);
sun.setPosition(sv);

Projector projector = new Projector();
projector.setClippingPlanes(0.001f, 100f);
projector.setFOVLimits(0, 999);
float fov = projector.convertDEGAngleIntoFOV(90);
projector.setFOV(fov);
projector.setYFOV(fov);

sh = new ShadowHelper(buffer, projector, 1024);
sh.setLightMode(true);
sh.setLightSource(projector);

sh.addCaster(myObject);
sh.addReceiver(plane);

MemoryHelper.compact();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}

public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
myObject.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
myObject.rotateX(touchTurnUp);
touchTurnUp = 0;
}

sh.updateShadowMap(buffer, world);

buffer.clear(back);

world.renderScene(buffer);
world.draw(buffer);

// sh.blit(buffer);

buffer.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
}
}
}


Apart from the shadow color, why there's no shadow at all when I set the projector's position in the sun's position ( projector.setPosition(sun.getPosition) ) ?
Isn't projection's position supposed to control the position of the shadow on the receiver? Just like what sun does with the ShadowHelper in your zip example?

EgonOlsen

The "hole in the ground" was caused a little flaw in the ShadowHelper. You could consider it to be a feature, but I've changed the behaviour anyway. Please re-download the jar: http://jpct.de/download/beta/jpct_ae.jar

Here's a slightly modified example:

package com.threed.jpct.examples;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Loader;
import com.threed.jpct.Logger;
import com.threed.jpct.Object3D;
import com.threed.jpct.Projector;
import com.threed.jpct.RGBColor;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.ExtendedPrimitives;
import com.threed.jpct.util.MemoryHelper;
import com.threed.jpct.util.ShadowHelper;

public class ShadowExample extends Activity {

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer buffer = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);

private float touchTurn = 0;
private float touchTurnUp = 0;

private float xpos = -1;
private float ypos = -1;

private Object3D myObject = null;
private Object3D plane = null;
private int fps = 0;

private Light sun = null;
private ShadowHelper sh;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());
mGLView.setEGLContextClientVersion(2);
renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);
}

@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}

@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}

@Override
protected void onStop() {
super.onStop();
System.exit(0);
}

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}

if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;

xpos = me.getX();
ypos = me.getY();

touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}

try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}

return super.onTouchEvent(me);
}

protected boolean isFullscreenOpaque() {
return true;
}

class MyRenderer implements GLSurfaceView.Renderer {

private long time = System.currentTimeMillis();

public MyRenderer() {
}

public void onSurfaceChanged(GL10 gl, int w, int h) {
try {
Config.farPlane = 5000;
buffer = new FrameBuffer(w, h);

world = new World();
world.setAmbientLight(100, 100, 100);

TextureManager tm = TextureManager.getInstance();
tm.addTexture("floor", new Texture(ShadowExample.this.getBaseContext().getAssets().open("floor.jpg")));

myObject = Object3D.mergeAll(Loader.loadOBJ(
getApplicationContext().getAssets().open("teapot.obj"), null, 12));
myObject.build();
myObject.translate(0, 10, 0);
world.addObject(myObject);

plane = ExtendedPrimitives.createPlane(100, 2);
plane.setTexture("floor");
plane.setTransparency(20);
plane.build();
plane.translate(0, 40, 10);
world.addObject(plane);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 50);
cam.lookAt(plane.getTransformedCenter());

SimpleVector sv = new SimpleVector(myObject.getTransformedCenter());
sv.y -= 100;
sv.z += 30;

sun = new Light(world);
sun.setIntensity(250, 250, 250);
sun.setPosition(sv);
 
Projector projector = new Projector();
projector.setClippingPlanes(1f, 300f);
projector.setFOVLimits(0, 999);
float fov = projector.convertDEGAngleIntoFOV(90);
projector.setFOV(fov);
projector.setYFOV(fov);

projector.setPosition(sun.getPosition());
projector.lookAt(myObject.getTransformedCenter());

sh = new ShadowHelper(buffer, projector, 1024);
sh.setLightSource(projector);
sh.setAmbientLight(new RGBColor(50,50,50));
sh.addCaster(myObject);
sh.addReceiver(plane);

MemoryHelper.compact();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}

public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
myObject.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
myObject.rotateX(touchTurnUp);
touchTurnUp = 0;
}

sh.updateShadowMap(buffer, world);

buffer.clear(back);

world.renderScene(buffer);
world.draw(buffer);

// sh.blit(buffer);

buffer.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
}
}
}


It places the projector at the sun's position. If you want to do that, you have to take two additional things into account:


  • The far clipping plane should be large enough to be able to render the object from the light source.
  • The direction of the projector is important. Make it look at the object (for example...it depends on your scene...just don't make it look up into the sky)

Hope this helps.

maggie