Path Tracing - Generate Camera Rays with a Left Handed coordinate system - rendering

Been having some issues implementing a camera for my renderer. As the question states,I would like to know the necessary steps to generate such a camera.With field of view and aspect ratio included.Its important that the Coordinate system be left handed such that -z pushes the camera away from the screen(as I understand it).I have tried looking online but most of the implementations are incomplete or have failed me.Any help is appreciated.Thank You.

I had trouble with this and took a long time to figure out. Here is the code for camera class.
#ifndef CAMERA_H_
#define CAMERA_H_
#include "common.h"
struct Camera {
Vec3fa position, direction;
float fovDist, aspectRatio;
double imgWidth, imgHeight;
Mat4 camMatrix;
Camera(Vec3fa pos, Vec3fa cRot, Vec3fa cDir, float cfov, int width, int height) {
position = pos;
aspectRatio = width / (float)height;
imgWidth = width;
imgHeight = height;
Vec3fa angle = Vec3fa(cRot.x, cRot.y, -cRot.z);
camMatrix.setRotationRadians(angle * M_PI / 180.0f);
direction = Vec3fa(0.0f, 0.0f, -1.0f);
camMatrix.rotateVect(direction);
fovDist = 2.0f * tan(M_PI * 0.5f * cfov / 180.0);
}
Vec3fa getRayDirection(float x, float y) {
Vec3fa delta = Vec3fa((x-0.5f) * fovDist * aspectRatio, (y-0.5f) * fovDist, 0.0f);
camMatrix.rotateVect(delta);
return (direction + delta);
}
};
#endif
Incase if you need the rotateVect() code in the Mat4 class
void Mat4::rotateVect(Vector3& vect) const
{
Vector3 tmp = vect;
vect.x = tmp.x * (*this)[0] + tmp.y * (*this)[4] + tmp.z * (*this)[8];
vect.y = tmp.x * (*this)[1] + tmp.y * (*this)[5] + tmp.z * (*this)[9];
vect.z = tmp.x * (*this)[2] + tmp.y * (*this)[6] + tmp.z * (*this)[10];
}
Here is our setRotationRadians code
void Mat4::setRotationRadians(Vector3 rotation)
{
const float cr = cos(rotation.x);
const float sr = sin(rotation.x);
const float cp = cos(rotation.y);
const float sp = sin(rotation.y);
const float cy = cos(rotation.z);
const float sy = sin(rotation.z);
(*this)[0] = (cp * cy);
(*this)[1] = (cp * sy);
(*this)[2] = (-sp);
const float srsp = sr * sp;
const float crsp = cr * sp;
(*this)[4] = (srsp * cy - cr * sy);
(*this)[5] = (srsp * sy + cr * cy);
(*this)[6] = (sr * cp);
(*this)[8] = (crsp * cy + sr * sy);
(*this)[9] = (crsp * sy - sr * cy);
(*this)[10] = (cr * cp);
}

Related

How to make a 2d shader working with ParallaxBackground node in Godot?

In my game I want to make a scrolling background with moving stars. I am using ParallaxBackground node with ParallaxLayer as a child, and the later has TextureRect child that display a 2d shader for the stars.
Nodes hierarchy:
ParallaxBackground -> StarsLayer -> Stars
Stars is the TextureRect and its rect_size equals the project window size.
Here is the 2d shader that it uses:
shader_type canvas_item;
uniform vec4 bg_color: hint_color;
float rand(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
void fragment() {
float size = 100.0;
float prob = 0.9;
vec2 pos = floor(1.0 / size * FRAGCOORD.xy);
float color = 0.0;
float starValue = rand(pos);
if (starValue > prob)
{
vec2 center = size * pos + vec2(size, size) * 0.5;
float t = 0.9 + 0.2 * sin(TIME * 8.0 + (starValue - prob) / (1.0 - prob) * 45.0);
color = 1.0 - distance(FRAGCOORD.xy, center) / (0.5 * size);
color = color * t / (abs(FRAGCOORD.y - center.y)) * t / (abs(FRAGCOORD.x - center.x));
}
else if (rand(SCREEN_UV.xy / 20.0) > 0.996)
{
float r = rand(SCREEN_UV.xy);
color = r * (0.85 * sin(TIME * (r * 5.0) + 720.0 * r) + 0.95);
}
COLOR = vec4(vec3(color),1.0) + bg_color;
}
Here is ParallaxBackground script:
extends ParallaxBackground
onready var stars_layer = $StarsLayer
var bg_offset = 0.0
func _ready():
stars_layer.motion_mirroring = Vector2(0, Helpers.WINDOW_SIZE.y)
func _process(delta):
bg_offset += 30 * delta
scroll_offset = Vector2(0, bg_offset)
The problem is that the stars are being showed but not moving at all.
Use motion_offset instead of scroll_offset
func _process(delta):
motion_offset += 30 * delta

move lat long by meters in direction and distance

Any idea how make it? My code looks like this,but it dont work. In attached photo where point 0 is center of circle and rest are circle points(every 45 degress). An example we see that points don't make circle
Look point 0 is center of circle
I pasting my code here:
static float[] RotateVector(float[] v, float degrees)
{
float sin = (float)Math.Sin(degrees * 0.0174553294f);
float cos = (float)Math.Cos(degrees * 0.0174553294f);
float tx = v[0];
float ty = v[1];
return new float[] { (cos * tx) - (sin * ty), (sin * tx) + (cos * ty) };
}
static void Main(string[] args)
{
float lat = 53.1324886f;
float lon = 23.1688403f;
float R = 6378137;
float distance = 100;
float dn = 0;
float de = 1;
float[] rotation = RotateVector(new float[] { dn, de }, 180);
rotation[0] = rotation[0] * distance;
rotation[1] = rotation[1] * distance;
float dLat = rotation[0] / R;
float dLon = rotation[1] / (R * (float)Math.Cos(Math.PI * lat / 180));
float latO = lat + dLat * 180 / (float)Math.PI;
float lonO = lon + dLon * 180 / (float)Math.PI;
Console.WriteLine(latO+" "+ lonO);
Console.ReadKey();
}
Website with map don't work correctly. On google maps code work fine.

Calculate vertical bearing between two GPS coordinates with altitudes

I am planning to build an antenna tracker. I need to get bearing and tilt from GPS point A with altitude and GPS point B with altitude.
This is the example points:
latA = 39.099912
lonA = -94.581213
altA = 273.543
latB = 38.627089
lonB = -90.200203
altB = 1380.245
I've already got the formula for horizontal bearing and it gives me 97.89138167122422
This is the code:
function toRadian(num) {
return num * (Math.PI / 180);
}
function toDegree(num) {
return num * (180 / Math.PI);
}
function getHorizontalBearing(fromLat, fromLon, toLat, toLon) {
fromLat = toRadian(fromLat);
fromLon = toRadian(fromLon);
toLat = toRadian(toLat);
toLon = toRadian(toLon);
let dLon = toLon - fromLon;
let x = Math.tan(toLat / 2 + Math.PI / 4);
let y = Math.tan(fromLat / 2 + Math.PI / 4);
let dPhi = Math.log(x / y);
if (Math.abs(dLon) > Math.PI) {
if (dLon > 0.0) {
dLon = -(2 * Math.PI - dLon);
} else {
dLon = (2 * Math.PI + dLon);
}
}
return (toDegree(Math.atan2(dLon, dPhi)) + 360) % 360;
}
let n = getHorizontalBearing(39.099912, -94.581213, 38.627089, -90.200203);
console.info(n);
But I don't know how to find the tilt angle. Anyone could help me?
I think I got the answer after searching around.
This is the complete code, if you think this is wrong, feel free to correct me.
function toRadian(num) {
return num * (Math.PI / 180);
}
function toDegree(num) {
return num * (180 / Math.PI);
}
// North is 0 degree, South is 180 degree
function getHorizontalBearing(fromLat, fromLon, toLat, toLon, currentBearing) {
fromLat = toRadian(fromLat);
fromLon = toRadian(fromLon);
toLat = toRadian(toLat);
toLon = toRadian(toLon);
let dLon = toLon - fromLon;
let x = Math.tan(toLat / 2 + Math.PI / 4);
let y = Math.tan(fromLat / 2 + Math.PI / 4);
let dPhi = Math.log(x / y);
if (Math.abs(dLon) > Math.PI) {
if (dLon > 0.0) {
dLon = -(2 * Math.PI - dLon);
} else {
dLon = (2 * Math.PI + dLon);
}
}
let targetBearing = (toDegree(Math.atan2(dLon, dPhi)) + 360) % 360;
return targetBearing - currentBearing;
}
// Horizon is 0 degree, Up is 90 degree
function getVerticalBearing(fromLat, fromLon, fromAlt, toLat, toLon, toAlt, currentElevation) {
fromLat = toRadian(fromLat);
fromLon = toRadian(fromLon);
toLat = toRadian(toLat);
toLon = toRadian(toLon);
let fromECEF = getECEF(fromLat, fromLon, fromAlt);
let toECEF = getECEF(toLat, toLon, toAlt);
let deltaECEF = getDeltaECEF(fromECEF, toECEF);
let d = (fromECEF[0] * deltaECEF[0] + fromECEF[1] * deltaECEF[1] + fromECEF[2] * deltaECEF[2]);
let a = ((fromECEF[0] * fromECEF[0]) + (fromECEF[1] * fromECEF[1]) + (fromECEF[2] * fromECEF[2]));
let b = ((deltaECEF[0] * deltaECEF[0]) + (deltaECEF[2] * deltaECEF[2]) + (deltaECEF[2] * deltaECEF[2]));
let elevation = toDegree(Math.acos(d / Math.sqrt(a * b)));
elevation = 90 - elevation;
return elevation - currentElevation;
}
function getDeltaECEF(from, to) {
let X = to[0] - from[0];
let Y = to[1] - from[1];
let Z = to[2] - from[2];
return [X, Y, Z];
}
function getECEF(lat, lon, alt) {
let radius = 6378137;
let flatteningDenom = 298.257223563;
let flattening = 0.003352811;
let polarRadius = 6356752.312106893;
let asqr = radius * radius;
let bsqr = polarRadius * polarRadius;
let e = Math.sqrt((asqr-bsqr)/asqr);
// let eprime = Math.sqrt((asqr-bsqr)/bsqr);
let N = getN(radius, e, lat);
let ratio = (bsqr / asqr);
let X = (N + alt) * Math.cos(lat) * Math.cos(lon);
let Y = (N + alt) * Math.cos(lat) * Math.sin(lon);
let Z = (ratio * N + alt) * Math.sin(lat);
return [X, Y, Z];
}
function getN(a, e, latitude) {
let sinlatitude = Math.sin(latitude);
let denom = Math.sqrt(1 - e * e * sinlatitude * sinlatitude);
return a / denom;
}
let n = getHorizontalBearing(39.099912, -94.581213, 39.099912, -94.588032, 0.00);
console.info("Horizontal bearing:\t", n);
let m = getVerticalBearing(39.099912, -94.581213, 273.543, 39.099912, -94.588032, 873.543, 0.0);
console.info("Vertical bearing:\t", m);
Don Cross's javascript code produces good results. It takes into consideration the curvature of the earth plus the fact that the earth is oblate.
Example:
var elDegrees = calculateElevationAngleCosineKitty(
{latitude: 35.346257, longitude: -97.863801, altitudeMetres: 10},
{latitude: 34.450545, longitude: -96.500167, altitudeMetres: 9873}
);
console.log("El: " + elDegrees);
/***********************************
Code by Don Cross at cosinekitty.com
http://cosinekitty.com/compass.html
************************************/
function calculateElevationAngleCosineKitty(source, target)
{
var oblate = true;
var a = {'lat':source.latitude, 'lon':source.longitude, 'elv':source.altitudeMetres};
var b = {'lat':target.latitude, 'lon':target.longitude, 'elv':target.altitudeMetres};
var ap = LocationToPoint(a, oblate);
var bp = LocationToPoint(b, oblate);
var bma = NormalizeVectorDiff(bp, ap);
var elevation = 90.0 - (180.0 / Math.PI)*Math.acos(bma.x*ap.nx + bma.y*ap.ny + bma.z*ap.nz);
return elevation;
}
function NormalizeVectorDiff(b, a)
{
// Calculate norm(b-a), where norm divides a vector by its length to produce a unit vector.
var dx = b.x - a.x;
var dy = b.y - a.y;
var dz = b.z - a.z;
var dist2 = dx*dx + dy*dy + dz*dz;
if (dist2 == 0) {
return null;
}
var dist = Math.sqrt(dist2);
return { 'x':(dx/dist), 'y':(dy/dist), 'z':(dz/dist), 'radius':1.0 };
}
function EarthRadiusInMeters (latitudeRadians) // latitude is geodetic, i.e. that reported by GPS
{
// http://en.wikipedia.org/wiki/Earth_radius
var a = 6378137.0; // equatorial radius in meters
var b = 6356752.3; // polar radius in meters
var cos = Math.cos (latitudeRadians);
var sin = Math.sin (latitudeRadians);
var t1 = a * a * cos;
var t2 = b * b * sin;
var t3 = a * cos;
var t4 = b * sin;
return Math.sqrt ((t1*t1 + t2*t2) / (t3*t3 + t4*t4));
}
function GeocentricLatitude(lat)
{
// Convert geodetic latitude 'lat' to a geocentric latitude 'clat'.
// Geodetic latitude is the latitude as given by GPS.
// Geocentric latitude is the angle measured from center of Earth between a point and the equator.
// https://en.wikipedia.org/wiki/Latitude#Geocentric_latitude
var e2 = 0.00669437999014;
var clat = Math.atan((1.0 - e2) * Math.tan(lat));
return clat;
}
function LocationToPoint(c, oblate)
{
// Convert (lat, lon, elv) to (x, y, z).
var lat = c.lat * Math.PI / 180.0;
var lon = c.lon * Math.PI / 180.0;
var radius = oblate ? EarthRadiusInMeters(lat) : 6371009;
var clat = oblate ? GeocentricLatitude(lat) : lat;
var cosLon = Math.cos(lon);
var sinLon = Math.sin(lon);
var cosLat = Math.cos(clat);
var sinLat = Math.sin(clat);
var x = radius * cosLon * cosLat;
var y = radius * sinLon * cosLat;
var z = radius * sinLat;
// We used geocentric latitude to calculate (x,y,z) on the Earth's ellipsoid.
// Now we use geodetic latitude to calculate normal vector from the surface, to correct for elevation.
var cosGlat = Math.cos(lat);
var sinGlat = Math.sin(lat);
var nx = cosGlat * cosLon;
var ny = cosGlat * sinLon;
var nz = sinGlat;
x += c.elv * nx;
y += c.elv * ny;
z += c.elv * nz;
return {'x':x, 'y':y, 'z':z, 'radius':radius, 'nx':nx, 'ny':ny, 'nz':nz};
}
/***********************
END cosinekitty.com code
************************/

opencl workitem run parallel

asking about speed or optimize the code
the kernel for sobel edge detection for gray img
When I run the program without any process only show input video and output(same as input) the frame per secounds fps=70 but when process down to 20 (process using GPU kernel for sobel)
Does anyone have an idea of how to speed up this code? I used local memory instead of global memory but the change is small.
How can I make all work items process the image?
sobel kernel
__kernel void hello_kernel(const __global uchar *input, __global uchar *output,const uint width,const uint height)
{
int x = get_global_id(0);
int y = get_global_id(1);
int index = width * y + x;
float a,b,c,d,e,f,g,h,i;
float8 v;
float sobelX = 0;
float sobelY = 0;
//if(index > width && index < (height*width)-width && (index % width-1) > 0 && (index % width-1) < width-1){
a = input[index-1-width] * -1.0f;
b =input[index-0-width] * 0.0f;
c = input[index+1-width] * +1.0f;
d = input[index-1] * -2.0f;
e = input[index-0] * 0.0f;
f = input[index+1] * +2.0f;
g = input[index-1+width] * -1.0f;
h = input[index-0+width] * 0.0f;
i = input[index+1+width] * +1.0f;
sobelX = a+b+c+d+e+f+g+h+i;
a = input[index-1-width] * -1.0f;
b = input[index-0-width] * -2.0f;
c = input[index+1-width] * -1.0f;
d = input[index-1] * 0.0f;
e = input[index-0] * 0.0f;
f = input[index+1] * 0.0f;
g = input[index-1+width] * +1.0f;
h = input[index-0+width] * +2.0f;
i = input[index+1+width] * +1.0f;
sobelY = a+b+c+d+e+f+g+h+i;
output[index] = sqrt(pow(sobelX,2) + pow(sobelY,2));
}

Determining Midpoint Between 2 Coordinates

I am trying to determine the midpoint between two locations in an MKMapView. I am following the method outlined here (and here) and rewrote it in Objective-C, but the map is being centered somewhere northeast of Baffin Island, which is no where near the two points.
My method based on the java method linked above:
+(CLLocationCoordinate2D)findCenterPoint:(CLLocationCoordinate2D)_lo1 :(CLLocationCoordinate2D)_loc2 {
CLLocationCoordinate2D center;
double lon1 = _lo1.longitude * M_PI / 180;
double lon2 = _loc2.longitude * M_PI / 100;
double lat1 = _lo1.latitude * M_PI / 180;
double lat2 = _loc2.latitude * M_PI / 100;
double dLon = lon2 - lon1;
double x = cos(lat2) * cos(dLon);
double y = cos(lat2) * sin(dLon);
double lat3 = atan2( sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y) );
double lon3 = lon1 + atan2(y, cos(lat1) + x);
center.latitude = lat3 * 180 / M_PI;
center.longitude = lon3 * 180 / M_PI;
return center;
}
The 2 parameters have the following data:
_loc1:
latitude = 45.4959839
longitude = -73.67826455
_loc2:
latitude = 45.482889
longitude = -73.57522299
The above are correctly place on the map (in and around Montreal). I am trying to center the map in the midpoint between the 2, yet my method return the following:
latitude = 65.29055
longitude = -82.55425
which somewhere in the arctic, when it should be around 500 miles south.
In case someone need code in Swift, I have written library function in Swift to calculate the midpoint between MULTIPLE coordinates:
// /** Degrees to Radian **/
class func degreeToRadian(angle:CLLocationDegrees) -> CGFloat {
return ( (CGFloat(angle)) / 180.0 * CGFloat(M_PI) )
}
// /** Radians to Degrees **/
class func radianToDegree(radian:CGFloat) -> CLLocationDegrees {
return CLLocationDegrees( radian * CGFloat(180.0 / M_PI) )
}
class func middlePointOfListMarkers(listCoords: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
var x = 0.0 as CGFloat
var y = 0.0 as CGFloat
var z = 0.0 as CGFloat
for coordinate in listCoords{
var lat:CGFloat = degreeToRadian(coordinate.latitude)
var lon:CGFloat = degreeToRadian(coordinate.longitude)
x = x + cos(lat) * cos(lon)
y = y + cos(lat) * sin(lon)
z = z + sin(lat)
}
x = x/CGFloat(listCoords.count)
y = y/CGFloat(listCoords.count)
z = z/CGFloat(listCoords.count)
var resultLon: CGFloat = atan2(y, x)
var resultHyp: CGFloat = sqrt(x*x+y*y)
var resultLat:CGFloat = atan2(z, resultHyp)
var newLat = radianToDegree(resultLat)
var newLon = radianToDegree(resultLon)
var result:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: newLat, longitude: newLon)
return result
}
Detailed answer can be found here
Updated For Swift 5
func geographicMidpoint(betweenCoordinates coordinates: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
guard coordinates.count > 1 else {
return coordinates.first ?? // return the only coordinate
CLLocationCoordinate2D(latitude: 0, longitude: 0) // return null island if no coordinates were given
}
var x = Double(0)
var y = Double(0)
var z = Double(0)
for coordinate in coordinates {
let lat = coordinate.latitude.toRadians()
let lon = coordinate.longitude.toRadians()
x += cos(lat) * cos(lon)
y += cos(lat) * sin(lon)
z += sin(lat)
}
x /= Double(coordinates.count)
y /= Double(coordinates.count)
z /= Double(coordinates.count)
let lon = atan2(y, x)
let hyp = sqrt(x * x + y * y)
let lat = atan2(z, hyp)
return CLLocationCoordinate2D(latitude: lat.toDegrees(), longitude: lon.toDegrees())
}
}
Just a hunch, but I noticed your lon2 and lat2 variables are being computed with M_PI/100 and not M_PI/180.
double lon1 = _lo1.longitude * M_PI / 180;
double lon2 = _loc2.longitude * M_PI / 100;
double lat1 = _lo1.latitude * M_PI / 180;
double lat2 = _loc2.latitude * M_PI / 100;
Changing those to 180 might help you out a bit.
For swift users, corrected variant as #dinjas suggest
import Foundation
import MapKit
extension CLLocationCoordinate2D {
// MARK: CLLocationCoordinate2D+MidPoint
func middleLocationWith(location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
let lon1 = longitude * M_PI / 180
let lon2 = location.longitude * M_PI / 180
let lat1 = latitude * M_PI / 180
let lat2 = location.latitude * M_PI / 180
let dLon = lon2 - lon1
let x = cos(lat2) * cos(dLon)
let y = cos(lat2) * sin(dLon)
let lat3 = atan2( sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y) )
let lon3 = lon1 + atan2(y, cos(lat1) + x)
let center:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat3 * 180 / M_PI, lon3 * 180 / M_PI)
return center
}
}
It's important to say that the formula the OP used to calculate geographic midpoint is based on this formula which explains the cos/sin/sqrt calculation.
This formula will give you the geographic midpoint for any long distance including the four quarters and the prime meridian.
But, if your calculation is for short-range around 1 Kilometer, using a simple average will produce the same midpoint results.
i.e:
let firstPoint = CLLocation(....)
let secondPoint = CLLocation(....)
let midPointLat = (firstPoint.coordinate.latitude + secondPoint.coordinate.latitude) / 2
let midPointLong = (firstPoint.coordinate.longitude + secondPoint.coordinate.longitude) / 2
You can actually use it for 10km but expect a deviation - if you only need an estimation for a short range midpoint with a fast solution it will be sufficient.
I think you are over thinking it a bit. Just do:
float lon3 = ((lon1 + lon2) / 2)
float lat3 = ((lat1 + lat2) / 2)
lat3 and lon3 will be the center point.