I can't seem to wrap my head around these shadow map matrices. It works great when light angles are about a 45 degree lookat position to 0,0,0. For example, if the sun angle is high(y) vs x,z. The shadows don't line up with the models. A light position of (-7,-10,7) or (-9,-10,0) works fine. (-1,-10,1) is skewed. Here is my code example. -x is left, -y is up and -z is far.
public Vector3f cameraPosition = new Vector3f(0f, -10f, 7f);
public float[] lightPosition = new float[]{-7f, -10.0f, 7f}
draw objects to shadow map texture
Matrix.setLookAtM(sunMatrix, 0, -lightPosition[0], lightPosition[1], lightPosition[2],
0, 0, 0,
0f, 1f, 0f);
//projection matrix plus sun
float width = (globals.glScreenSize/2);
float height = (globals.glScreenSize/2);
Matrix.orthoM(projectionMatrix, 0, -width, width,
-height, height,
-1f, 100f);
tempMatrix = new float[16];
Matrix.multiplyMM(tempMatrix, 0, projectionMatrix, 0, sunMatrix, 0);//add camera matrix to perspective
projectionMatrix = tempMatrix.clone();
//translate
Matrix.setIdentityM(modelMatrix, 0);//set to 0
//translate
Matrix.translateM(modelMatrix, 0, modelPos.location.x,
-(modelPos.location.y),
modelPos.location.z);//move
//rotate
Matrix.rotateM(modelMatrix, 0, modelPos.angles.x, 1f, 0f, 0f);
Matrix.rotateM(modelMatrix, 0, -modelPos.angles.y, 0f, 1f, 0f);
Matrix.rotateM(modelMatrix, 0, -modelPos.angles.z, 0f, 0f, 1f);
//scale
Matrix.scaleM(modelMatrix, 0, modelPos.scales.x,
modelPos.scales.y,
modelPos.scales.z);//scale
Matrix.multiplyMM(viewProjMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
//Matrix.multiplyMM(projectionMatrix, 0, globals.viewProjMatrix, 0, modelMatrix, 0);//perspective/model/view projection matrix
finalMatrix = viewProjMatrix.clone();//final matrix created
rotate around y axis and draw shadow map to screen
Matrix.setLookAtM(modelMatrix, 0, 0f, 0f, 0f,
globals.lightPosition[0], 0f, -globals.lightPosition[2],
0f, 1f, 0f);
//scale
Matrix.scaleM(modelMatrix, 0, scale.x, scale.y, scale.y);//scale
Matrix.multiplyMM(projectionMatrix, 0, globals.viewProjMatrix, 0, modelMatrix, 0);//projection matrix
finalMatrix = projectionMatrix.clone();//final matrix created
enter image description here
Thanks for any help,
Norm
I changed,
float height = (globals.glScreenSize/2) * Math.abs(lightPos.y);
Shadows all line up with the models now. If you are looking for a simple shadow map and obj loader example, check out https://github.com/Normfly/myOpenglES20
Happy coding,
Norm
Related
I want to make a loading animation in After Effects using scripts
I get no arc only white screen with shape layer there
and this is what gets rendered in the app
This is my code that doesn't work
app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
app.newProject();
app.beginUndoGroup("Create Comp");
// Create a new composition with a solid layer
var comp = app.project.items.addComp("My Composition", 1920, 1080, 1, 10, 24);
comp.openInViewer();
var solid = comp.layers.addSolid([1, 1, 1], "My Solid", 1920, 1080, 1, 10);
// Create a new shape layer
var shapeLayer = comp.layers.addShape();
// shapeLayer.moveToStart();
shapeLayer.enabled= true;
// Set position of the shape layer
var shapePosition = shapeLayer.property("ADBE Transform Group").property("ADBE Position");
shapePosition.setValue([960,540]);
var path = shapeLayer.property("ADBE Root Vectors Group").addProperty("ADBE Vector Shape - Group");
// make a arc
path.property("ADBE Vector Shape").setValue(new Shape());
path.property("ADBE Vector Shape").value.vertices = [[0, 0], [100, 100], [200, 0]];
path.property("ADBE Vector Shape").value.inTangents = [[-50, -50], [0, 0]];
path.property("ADBE Vector Shape").value.outTangents = [[0, 0], [50, 50]];
path.property("ADBE Vector Shape").value.closed = true;
path.enabled = true;
var stroke = shapeLayer.property("ADBE Root Vectors Group").addProperty("ADBE Vector Graphic - Stroke");
stroke.property("ADBE Vector Stroke Width").setValue(5);
stroke.property("ADBE Vector Stroke Color").setValue([1, 0, 0]);
stroke.enabled = true;
var fill = shapeLayer.property("ADBE Root Vectors Group").addProperty("ADBE Vector Graphic - Fill");
fill.property("ADBE Vector Fill Color").setValue([1, 0, 0]);
fill.enabled = true;
// Create an animation for the rotation property
var rotation = shapeLayer.property("ADBE Transform Group").property("ADBE Rotate Z");
rotation.setValueAtTime(0, 0);
rotation.setValueAtTime(5, 180);
// Set the composition duration to 10 seconds
comp.duration = 10;
app.endUndoGroup();
Properties in AE can't be set by assigning values to them. You need to use the setValue() method. Additionally in this instance you want to set the values of the shape object before assigning it to the path property.
var theShape = new Shape();
theShape.vertices = [[0, 0], [100, 100], [200, 0]];
theShape.inTangents = [[-50, -50], [0, 0]];
theShape.outTangents = [[0, 0], [50, 50]];
theShape.closed = true;
path.property("ADBE Vector Shape").setValue(theShape);
Also, as an AE developer:
I am working with motion capture data and I want to do "skinning" in processing. So basically with every two points I get from my data I have to add a 3D object in between (I'll be using a box for now and the placement and rotation coordinates are the center of the 3D object) and rotate it so that it is aligned in all three dimensions with the vector that connects the two points.
Here we can see on the left, the initial placed box between the two points and on the right the now correctly rotated box:
The only way I know of to rotate an object in processing is to use the rotateX(), rotateY(), rotateZ() functions, which rotate an object around the global(?) axes using euler angles.
Now I am seriously struggling with finding a way to calculate this rotation properly.
I have already written a function for calculating the angle between two vectors:
float calcVectorAngle(PVector p1, PVector p2) {
return acos((p1.dot(p2)) / (mag(p1.x, p1.y, p1.z) * mag(p2.x, p2.y, p2.z)));
}
And I then tried to feed the vector (between the two points) combined with a unit vector for one of each rotation axis:
float x = calcVectorAngle(vector, new PVector(1,0,0));
float y = calcVectorAngle(vector, new PVector(0,1,0));
float z = calcVectorAngle(vector, new PVector(0,0,1));
But when I use these values to rotate the object the rotation is completely off.
A code example:
PVector p1;
PVector p2;
PVector boxSize = new PVector(500, 100, 100);
void setup() {
size(1000,1000,P3D);
p1 = new PVector(100, 100, 0);
p2 = new PVector(900, 900, -1000);
}
void draw() {
background(125);
strokeWeight(2);
stroke(255, 0, 0);
pushMatrix();
line(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
PVector midPoint = calcMidPoint(p1, p2);
translate(midPoint.x, midPoint.y, midPoint.z);
PVector rotation = calcRotation(p1, p2);
rotateX(rotation.x);
rotateY(rotation.y);
rotateZ(rotation.z);
box(boxSize.x, boxSize.y, boxSize.z);
popMatrix();
}
PVector calcMidPoint(PVector p1, PVector p2) {
return new PVector((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2);
}
PVector calcRotation(PVector p1, PVector p2) {
PVector vector = new PVector();
vector.sub(p2, p1, vector);
float x = calcVectorAngle(vector, new PVector(1,0,0));
float y = calcVectorAngle(vector, new PVector(0,1,0));
float z = calcVectorAngle(vector, new PVector(0,0,1));
return new PVector(x, y, z);
}
float calcVectorAngle(PVector p1, PVector p2) {
return acos((p1.dot(p2)) / (mag(p1.x, p1.y, p1.z) * mag(p2.x, p2.y, p2.z)));
}
Now I am a little lost.
The axis of rotation is the Cross product of the default direction of the box (1, 0, 0) and the direction along the line (p2 - p1).
The angle of rotation is the acos of the Dot product of the normalized direction vectors:
PVector currentDirection = new PVector(1, 0, 0);
PVector newDirection = p2.copy().sub(p1).normalize();
PVector rotationAxis = currentDirection.cross(newDirection).normalize();
float rotationAngle = acos(currentDirection.dot(newDirection));
Rotate the box by the angle around the axis:
rotate(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
Complete example:
PVector p1, p2;
PVector boxSize = new PVector(500, 100, 100);
void setup() {
size(1000,1000,P3D);
p1 = new PVector(100, 100, 0);
p2 = new PVector(900, 900, -1000);
}
void draw() {
background(125);
strokeWeight(2);
stroke(255, 0, 0);
line(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
PVector midPoint = p1.copy().add(p2).mult(0.5);
PVector currentDirection = new PVector(1, 0, 0);
PVector newDirection = p2.copy().sub(p1).normalize();
PVector rotationAxis = currentDirection.cross(newDirection).normalize();
float rotationAngle = acos(currentDirection.dot(newDirection));
pushMatrix();
translate(midPoint.x, midPoint.y, midPoint.z);
rotate(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
box(boxSize.x, boxSize.y, boxSize.z);
popMatrix();
}
the inner shadow disappears during the transition it looks like the inner shadow is also scaled.
public void showTowerRange(int x0, int y0, double range) {
Circle circle = new Circle(
x0 * MAP_CELLS_WIDTH + MAP_CELLS_WIDTH / 2,
y0 * MAP_CELLS_HEIGHT + MAP_CELLS_HEIGHT / 2,
1,
Color.RED
);
circle.setOpacity(0.5);
gameRoot.getChildren().add(circle);
ScaleTransition scl = new ScaleTransition(Duration.millis(SHOW_RANGE_EFFECT_DURATION),circle);
scl.setByX(range * MAP_CELLS_WIDTH);
scl.setByY(range * MAP_CELLS_HEIGHT);
FadeTransition fd = new FadeTransition(Duration.millis(SHOW_RANGE_EFFECT_DURATION),circle);
fd.setByValue(-.3);
circle.setEffect(new InnerShadow(BlurType.GAUSSIAN, Color.GREEN, 4, 1, 0, 0));
circle.setEffect(new DropShadow(BlurType.GAUSSIAN, Color.WHITE, 2, 0, 0, 0));
ParallelTransition prl = new ParallelTransition(scl,fd);
prl.play();
}
Well, I have good news and bad news for you. The good one - scale transition does not ruin inner shadow. The bad one - it is you ruining inner shadow:)
The point is, setEffect() method does not add another effect, it just set effect property of the Node, replacing previous value. What you need to do is to chain you effects (see example here). So instead of this:
circle.setEffect(new InnerShadow(BlurType.GAUSSIAN, Color.GREEN, 4, 1, 0, 0));
circle.setEffect(new DropShadow(BlurType.GAUSSIAN, Color.WHITE, 2, 0, 0, 0));
You must do this:
InnerShadow is = new InnerShadow(BlurType.GAUSSIAN, Color.GREEN, 4, 1, 0, 0);
DropShadow ds = new DropShadow(BlurType.GAUSSIAN, Color.WHITE, 2, 0, 0, 0);
ds.setInput(is);
circle.setEffect(ds);
I'm making a particle-attractor-thing in Processing 3.0, and I'm having difficulty when adding multiples of what I've deemed 'Nodes'; objects that automatically attract or repel particles without mouse input. Normally left-clicking the mouse attracts these particles and right-clicking repels them. I made Nodes so that I could have multiple locations attracting/repelling at once.
The main problem I'm having atm is when I add more than one Node, only the last-added one actually works; all others do nothing.
Here is all my code including two other classes (Particle and Node). It's a lot of code, but I'd really appreciate it if someone could point out my error(s).
http://pastebin.com/iKELuVJ7
I believe the trouble is in the Particle class in the Node for loop where I am setting acc = d, but I don't know a good workaround.
As an additional question, does anyone know a good way to make the strength of the attraction scale inversely with distance from the attracting object? I tried a backwards map() function with arbitrary upper/lower limits which works for now, but I'd prefer not to hardcode it. Any other general improvements/recommendations are appreciated.
That's a nice looking sketch. You are totally right about acc = d.
This part:
for (Node n: nodes) {
PVector d = PVector.sub(n.loc, loc);
d.normalize();
d.mult(n.strength);
acc = d;
}
means you're overwriting the acc vector which d.
Since you run loop, in the end acc will be equal to the last node in your list.
If you want each node to have a certain influence, simply add the node based vector to acc:
for (Node n: nodes) {
PVector d = PVector.sub(n.loc, loc);
d.normalize();
d.mult(n.strength);
acc.add(d);
}
You did this already on the mouse interaction.
In terms of other recommendations, you could try to cache PVector instances more often. Bellow I supply a basic example of how to cache the mousePosition (as a single top level variable, rather than multiple instances, per particle, per frame). mag() uses sqrt() which can be costly. Try using magSq() instead (and using squared ranges instead). You can spot these this in chapter 6.15 A Few Last Notes: Optimization Tricks of Nature of Code by Daniel Shiffman. I warmly recommend this book.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PVector m = new PVector(mouseX, mouseY);
ArrayList<Particle> parts = new ArrayList<Particle>();
ArrayList<Node> nodes = new ArrayList<Node>();
float speed = 2;
float grav = 4;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
size(800, 800, P2D);
// fullScreen(P2D);
noStroke();
smooth(4);
blendMode(ADD);
frameRate(60);
for (int i = 0; i < 1000; i++) {
parts.add(new Particle(new PVector(random(width), random(height)), (int)random(1, 25)));
}
nodes.add(new Node(new PVector(200, 400), 0.75));
nodes.add(new Node(new PVector(400, 400), 0.50));
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void draw() {
background(0);
if(mousePressed){
m.set(mouseX, mouseY);
}
for (Node n: nodes) {
n.draw();
}
for (int i = 0; i < parts.size(); i++) {
Particle p = (Particle) parts.get(i);
p.draw();
p.boundary();
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Particle {
PVector loc, vel, acc;
int size = 5;
Particle(PVector loc, int size) {
this.loc = loc;
vel = new PVector();
acc = new PVector();
this.size = size;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void draw() {
acc.mult(0);
for (Node n: nodes) {
PVector d = PVector.sub(n.loc, loc);
d.normalize();
d.mult(n.strength);
//acc = d;
acc.add(d);
}
if (mousePressed && mouseButton == LEFT) {
PVector d = PVector.sub(m, loc);
//d.normalize();
d.setMag(map(d.mag(), 0, 2200, speed, 0));
d.mult(speed);
//acc = d;
acc.add(d);
} else if (mousePressed && mouseButton == RIGHT) {
PVector d = PVector.sub(m, loc);
d.normalize();
d.mult(-1*speed);
//acc = d;
acc.add(d);
}
acc.set(acc);
acc.mult(acc.mag());
vel.add(acc);
//vel.add(new PVector(0, grav));
loc.add(vel);
acc.mult(0);
//vel.mult(pow(0.90, 0.1*size));
vel.mult(0.97);
colorScheme(0);//"RedYellow"
ellipse(loc.x, loc.y, size, size);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void boundary() {
if(loc.x < 0)
vel.x *= -1;
if(loc.x > width)
vel.x *= -1;
if(loc.y < 0)
vel.y *= -1;
if(loc.y > height)
vel.y *= -1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
String[] patterns = {"RedYellow","RedMagenta","GreenYellow","GreenCyan","BlueMagenta","BlueCyan","Cyclic","BWCyclic","PROTOTYPE"};
//void colorScheme(String pattern) {
void colorScheme(int pattern) {
//compute this once, rather than multiple times per case
//also check out magSq() https://processing.org/reference/PVector_magSq_.html
float mag = vel.mag();
switch (pattern) {
case 0:
fill(255, map(mag, 0, 20, 0, 255), map(mag - 20, 0, 20, 0, 255)); //Red Yellow White
break;
case 1:
fill(255, map(mag - 20, 0, 20, 0, 255), map(mag, 0, 20, 0, 255)); //Red Magenta White
break;
case 2:
fill(map(mag, 0, 20, 0, 255), 255, map(mag - 20, 0, 20, 0, 255)); //Green Yellow White
break;
case 3:
fill(map(mag - 20, 0, 20, 0, 255), 255, map(mag, 0, 20, 0, 255)); //Green Cyan White
break;
case 4:
fill(map(mag, 0, 20, 0, 255), map(mag - 20, 0, 20, 0, 255), 255); //Blue Magenta White
break;
case 5:
fill(map(mag - 20, 0, 20, 0, 255), map(mag, 0, 20, 0, 255), 255); //Blue Cyan White
break;
case 6:
fill(cos(map(mag, 0, 20, 0, 360))*255, cos(map(mag + 20/3, 0, 20, 0, 360))*255, cos(map(mag + 40/3, 0, 20, 0, 360))*255); //Cyclic
break;
case 7:
fill(cos(map(mag, 0, 20, 0, 360))*255, cos(map(mag, 0, 20, 0, 360))*255, cos(map(mag, 0, 20, 0, 360))*255); //B&W Cyclic
break;
case 8:
fill(0, 0, 0); //Cyclic
break;
default:
stroke(255, 255, 255);
fill(255, 255, 255);
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Node {
PVector loc;
float strength;
Node(PVector loc, float strength) {
this.loc = loc;
this.strength = strength;
}
void draw() {
fill(255, 255, 255,64);
ellipse(loc.x, loc.y, 50, 50);
}
}
I have a gradient, e. g. green to red which ranges from 0 to 100. I need to lookup a color out of that gradient for any given value. Currently I'm painting a line on a canvas, fill it, take a snapshot and use a pixelreader to get the color. Does anyone know a better way? It seems like overkill to me.
A simple version of the code:
private Color getColor( double value) {
Canvas canvas = new Canvas(100, 1);
GraphicsContext gc = canvas.getGraphicsContext2D();
Stop[] stops = new Stop[] { new Stop(0, Color.GREEN), new Stop(1, Color.RED)};
LinearGradient linearGradient = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, stops);
gc.setFill(linearGradient);
gc.rect( 0, 0, canvas.getWidth(), canvas.getHeight());
gc.fill();
WritableImage image = new WritableImage((int) canvas.getWidth(), (int) canvas.getHeight());
image = canvas.snapshot(null, image);
PixelReader imageReader = image.getPixelReader();
Color imageColor = imageReader.getColor( (int) value, 0);
}
Thank you very much!
You can interpolate the color yourself:
Color imageColor = Color.GREEN.interpolate(Color.RED, value / 100.0);