spritesheet with instancing in webgl - webgl

I don't quite get how to pass in attribute data for rendering a sprite sheet or texture atlas while instancing. Let's assume WebGl1 (so no texture layers)
Without instancing, I have these uv coordinates to pass in:
let left = cell_x / atlas_width;
let right = (cell_x + cell_width) / atlas_width;
let top = (atlas_height - cell_y) / atlas_height;
let bottom = (atlas_height - (cell_y + cell_height)) / atlas_height;
let uvs = [left, top, left, bottom, right, top, right, bottom]
But with instancing, I don't quite get how to do that... my understanding is the attributes must be identical per-instance?
I tried to think about translating the above to shader code, like maybe a multiplier of some constant... but I couldn't quite figure it out.
I can get the left,right,top, and bottom since those only depend on the constants cell_* and atlas_*, but how do I get from knowing these corners to the actual uv output, since I don't know which vertex I'm in?
Any help is appreciated. Thanks!

Related

Trouble implementing shadows in WebGL

I am trying to implement shadows into my WEBGL 2.0 Project using this tutorial
https://webgl2fundamentals.org/webgl/lessons/webgl-shadows.html
Currently I am getting really bad results like this:
Basically a ton of the terrain is being drawn in shadow that shouldn't be. The light projection is from your camera towards the direction you are looking so hypothetically you shouldn't be able to see any shdaows becuase the light projection is the same as your camera ( I am just doing this for testing until I can get this working properly)
I have everything the same as the tutorial I believe except I am using glMatrix instead of their matrix math library (shouldn't matter I would assume). Here's the thing though. I don't use a model view matrix for anything I am rendering so none of my points are on a -1,1 range. They can go out as far as -3200...ect Its just all one big terrain mesh chunked out.
I think the issue lies with how I am creating the texture matrix
textureMatrix = glMatrix.mat4.create();
glMatrix.mat4.translate(textureMatrix,textureMatrix,[0.5,0.5,0.5]);
glMatrix.mat4.scale(textureMatrix,textureMatrix,[0.5,0.5,0.5]);
glMatrix.mat4.multiply(textureMatrix,textureMatrix, projectionMatrix);
glMatrix.mat4.invert(lightMatrix,lightMatrix);
glMatrix.mat4.multiply(textureMatrix,textureMatrix, lightMatrix);
I am using the same matrix for the light projection as your normal projection, is that an issue? if anyone could help it would be greatly appreciated.
That's probably because the Y position of your light (in your example, it is much more the distance between the eye and the scene) is too big for the Z size of your shadow volume (the size of your shadow volume in the view direction.) Here if posY is inside the wireframe box :
But if you increase posY too much (i.e. your shapes get out of the shadow volume, they disappear
So you should increase the size of your shadow volume (or shrinken your scene, either way.) You cannot simulate that with the slider because they just give you the control to the two dimensions X and Y dimensions : projWidth and projHeight.
i.e. in the last code in your tutorial page, the latest parameter ("far") for example change it from 10 to 100
const lightProjectionMatrix = settings.perspective
? m4.perspective(
degToRad(settings.fieldOfView),
settings.projWidth / settings.projHeight,
0.5, // near
10) // far
: m4.orthographic(
-settings.projWidth / 2, // left
settings.projWidth / 2, // right
-settings.projHeight / 2, // bottom
settings.projHeight / 2, // top
0.5, // near
100); // far
Then you can increase posY far more :
without having your full code, it is hard to reproduce and help you. Could you not try to just inject your scene into the tutorial code ? You can bind the viewpoint with the source and orientation of the light by using the same inputs : (just adding 0.5 to X to see a bit of shadow and make sure it is properly computed.)
/*const cameraPosition = [settings.cameraX, settings.cameraY, 15];*/
const cameraPosition = [settings.posX+0.5, settings.posY, settings.posZ];
/*const target = [0, 0, 0]; */
const target = [settings.targetX, settings.targetY, settings.targetZ];

Polar transformation in iOS

Can anyone help me to achieve this kind of animated transformation through Core Graphics. Look at the rough sketch:
This is a simple chart graphic, and I need to transform a histogramm-style bar (left shape) to a pie chart (right shape).
Literally the task is to roll a rectangle to a ring with a smooth animation. I almost found the way to do this with a tricky queue of drawings, mask clippings and affine transformations but this won't look exactly how I want it to look.
This is an interesting challenge, especially as you want to maintain the different segments. I won't give you a full answer (i.e full example code to achieving this), but I will explain what I think needs to be done to achieve the effect that you want.
Paths
First, I see both of these diagrams as a single line that is stroked (let's ignore the segments for a moment), so the challenge is going from a straight line to an enclosed circle.
I propose the following two paths, that you can animate between to achieve a nice wrapping effect.
// As we render a circle as a chain of straight line segments
// changing the count of said segments makes the circle more or less smooth
// Try this with other values, such as 8 or 32
let segments = 72
// With this segment count, the angle of each segment in the circle is
let angle = (2 * CGFloat(M_PI)) / CGFloat(segments)
// First path (straight)
let length = CGFloat(300.0)
let segmentLength = length / CGFloat(segments)
let straightPath = CGPathCreateMutable()
CGPathMoveToPoint(straightPath, nil, 0.0, 0.0)
for i in 0...segments {
CGPathAddLineToPoint(straightPath, nil, 0.0, CGFloat(i) * segmentLength)
}
// Second path (circle)
let radius = CGFloat(100.0)
let center = CGPoint(x: 104.0, y: 104.0)
let space = (x: 2.0, y: 2.0)
var circlePath = CGPathCreateMutable()
CGPathMoveToPoint(circlePath, nil, center.x + radius, center.y)
for i in 0...segments {
let x = cos(-CGFloat(i) * angle)
let y = sin(-CGFloat(i) * angle)
CGPathAddLineToPoint(circlePath, nil, center.x + radius * x, center.y + radius * y)
}
I have also uploaded a Swift plaground for you to experiment with, which you can find here
Segments
Now, handling the segments can be a bit tricky, however I propose a relatively naive implementation that might work. Mainly, CAShapeLayer has the following two properties - strokeStart and strokeEnd, which allow controlling the part of the path that is actually stroked.
With this in mind, you could create as many layers as there are segments, assign them all the same path(s) and tweak their respective strokeStart and strokeEnd properties to make it look the way you expect. Somewhat similar to what they do in this post.
Animation
Assuming you have conquered the previous two aspects, the animation aspect should be relatively straight forward, using the two types of paths you have, you can create a simple CABasicAnimation that goes from one to another. I will assume you are familiar with CoreAnimation and its usage (i.e how to properly change model values to match those that are presented etc.).
I will end my answer with a quick animation showing what the end result could look like (minus the segments), I have frozen the animation and am manipulating the timeOffset property of the layer to manually scrub through it.
I hope my answer helps you get closer to the solution you want. It is also important to emphasise that my code examples are just a beginning, you will likely need to tweak the values quite a bit to achieve a satisfying animation (for example, the length of the line should be similar to that of the circumference of the circle).
Skewing, twisting and bending are none trivial transformations on bodies.
These can't be done Core Graphics.
Better draw the chart yourself with CGContextAddArcToPoint in core graphic and mask out the inner circle.
The other (hardcore) way would be using a 3d engine - i.e. scene kit - and apply your chart as texture to it.

Quaternions, rotate a model and align with a direction

Suppose you have quaternion that describes the rotation of a 3D Model.
What I want to do is, given an Object (with rotationQuaternion, side vector...), I want to align it to a target point.
For a spaceship, I want the cockpit to point to a target.
Here is some code I have ... It's not doing what I want and I don't know why...
if (_target._ray.Position != _obj._ray.Position)
{
Vector3 vec = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
float angle = (float)Math.Acos(Vector3.Dot(vec, _obj._ray.Direction));
Vector3 cross = Vector3.Cross(vec, _obj._ray.Direction);
if (cross == Vector3.Zero)
cross = _obj._side;
_obj._rotationQuaternion *= Quaternion.CreateFromAxisAngle(cross,angle);
}
// Updates direction, up, side vectors and model Matrix
_obj.UpdateMatrix();
after some time the rotationQuaternion is filled with almost Zero at X,Y,Z and W
Any help?
Thanks ;-)
This is a shortcut I've used to get the quaternion for lock-on-target rotation:
Matrix rot = Matrix.CreateLookAt(_arrow.Position, _cube.Position, Vector3.Down);
_arrow.Rotation = Quaternion.CreateFromRotationMatrix(rot);
For this example, I'm rendering an arrow and a cube, where the cube is moving around in a circle, and with the above code the arrow is always pointing at the cube. (Though I imagine there are some edge cases when cube is exactly above or below).
Once you get this quaternion (from spaceship to target), you can use Quaternion.Lerp() to interpolate between current ship rotation and the aligned one. This will give your rotation a smooth transition (not just snap to target).
Btw, might be that your rotation gets reduced to zero because you're using *= when assigning to it.
Your code's a bit funky.
if (_target._ray.Position != _obj._ray.Position)
{
This may or may not be correct. Clearly, you've overridden the equals comparator. The correct thing be be doing here would be to ensure that the dot-product between the two (unit-length) rays is close to 1. If the rays have the same origin, then presumably have equal 'positions' means they're the same.
Vector3 vec = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
This seems particularly wrong. Unless the minus operator has been overridden in a strange way, subtracting this way doesn't make sense.
Here's pseudocode for what I recommend:
normalize3(targetRay);
normalize3(objectRay);
angleDif = acos(dotProduct(targetRay,objectRay));
if (angleDif!=0) {
orthoRay = crossProduct(objectRay,targetRay);
normalize3(orthoRay);
deltaQ = quaternionFromAxisAngle(orthoRay,angleDif);
rotationQuaternion = deltaQ*rotationQuaternion;
normalize4(rotationQuaternion);
}
Two things to note here:
Quaternions are not commutative. I've assumed that your quaternions are rotating column vectors; so I put deltaQ on the left. It's not clear what your *= operator is doing.
It's important to regularly normalize your quaternions after multiplication. Otherwise small errors accumulate and they drift away from unit length causing all manner of grief.
OMG! It worked!!!
Vector3 targetRay = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
Vector3 objectRay = Vector3.Normalize(_obj._ray.Direction);
float angle = (float)Math.Acos(Vector3.Dot(targetRay, objectRay));
if (angle!=0)
{
Vector3 ortho = Vector3.Normalize(Vector3.Cross(objectRay, targetRay));
_obj._rotationQuaternion = Quaternion.CreateFromAxisAngle(ortho, angle) * _obj._rotationQuaternion;
_obj._rotationQuaternion.Normalize();
}
_obj.UpdateMatrix();
Thank you very much JCooper!!!
And niko I like the idea of Lerp ;-)

The size of the terrain rendered from heightmap

I'm quite new to XNA so excuse me if I ask a 'silly' question but I couldn't find an answer.
I have a problem with the terrain rendered from a heightmap: the terrain I get is too small, I need something larger for my game but I'd like to keep the heigh tdata updated - so I can check for collisions later. (height data being a 2 dimensional array which holds the heights of each point - in my program it's called 'dateInaltime').
The problem is that if I modify the scale of the terrain, the collision checker will use the old values (from the original/small terrain) so I'll get wrong collision points.
My terrain class looks like this.
How can I make the terrain larger but also extend the height data array?
Change this part:
vertex[x + y * lungime].Position = new Vector3(x, dateInaltime[x, y], -y);
to:
vertex[x + y * lungime].Position = new Vector3(x, dateInaltime[x, y], -y) * new Vector3(10);
It should separate the vertices by a scale of 10 (or whatever number you choose).

Repeating 2d world

How to make a 2d world with fixed size, which would repeat itself when reached any side of the map?
When you reach a side of a map you see the opposite side of the map which merged togeather with this one. The idea is that if you didn't have a minimap you would not even notice the transition of map repeating itself.
I have a few ideas how to make it:
1) Keeping total of 3x3 world like these all the time which are exactly the same and updated the same way, just the players exists in only one of them.
2) Another way would be to seperate the map into smaller peaces and add them to required place when asked.
Either way it can be complicated to complete it. I remember that more thatn 10 years ago i played some game like that with soldiers following each other in a repeating wold shooting other AI soldiers.
Mostly waned to hear your thoughts about the idea and how it could be achieved. I'm coding in XNA(C#).
Another alternative is to generate noise using libnoise libraries. The beauty of this is that you can generate noise over a theoretical infinite amount of space.
Take a look at the following:
http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile
There is also an XNA port of the above at: http://bigblackblock.com/tools/libnoisexna
If you end up using the XNA port, you can do something like this:
Perlin perlin = new Perlin();
perlin.Frequency = 0.5f; //height
perlin.Lacunarity = 2f; //frequency increase between octaves
perlin.OctaveCount = 5; //Number of passes
perlin.Persistence = 0.45f; //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;
//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);
//Get a section
_map.GeneratePlanar(left, right, top, down);
GeneratePlanar is the function to call to get the sections in each direction that will connect seamlessly with the rest of your world.
If the game is tile based I think what you should do is:
Keep only one array for the game area.
Determine the visible area using modulo arithmetics over the size of the game area mod w and h where these are the width and height of the table.
E.g. if the table is 80x100 (0,0) top left coordinates with a width of 80 and height of 100 and the rect of the viewport is at (70,90) with a width of 40 and height of 20 you index with [70-79][0-29] for the x coordinate and [90-99][0-9] for the y. This can be achieved by calculating the index with the following formula:
idx = (n+i)%80 (or%100) where n is the top coordinate(x or y) for the rect and i is in the range for the width/height of the viewport.
This assumes that one step of movement moves the camera with non fractional coordinates.
So this is your second alternative in a little bit more detailed way. If you only want to repeat the terrain, you should separate the contents of the tile. In this case the contents will most likely be generated on the fly since you don't store them.
Hope this helped.

Resources