# OpenGL Roll from view, right, up vectors - 3d space?

This topic is 3020 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I have a camera defined through three vectors for view, right and up. The camera itself appears to be working fine. However when I try to determine the roll of the camera, compared to the horizon, I am running into some issues. (The reason I am doing trying to do this is to be able to draw a horizon reference line on the display.) I think I have myself pretty confused at the moment, I have a feeling the answer is relatively simple, I just can't see it. My camera vectors are: viewDir, rightVector and upVector; and since I am implementing in OpenGL they initially are set to: viewDir = new Vector3( 0, 0, -1); rightVector = new Vector3( 1, 0, 0); upVector = new Vector3( 0, 1, 0); I'm calculating heading and pitch with the following:
public double GetHeading()
{
return Math.Atan2(viewDir.X, -viewDir.Z);
}

public double GetPitch()
{
return Math.Asin(viewDir.Y);
}

Everything I have tried to calculate the roll relative to the horizon has failed in one way or another. In my current approach (which is probably overcomplicated) I do the following:
public double GetRoll()
{
// initialize temporary vectors
Vector3 tmpView = new Vector3(0,0,-1);
Vector3 tmpRight = new Vector3(1,0,0);
Vector3 tmpUp = new Vector3(0,1,0);

double P = GetPitch();

// rotate around up vector (heading)
tmpView = Vector3.Normalize(
(tmpView * Math.Cos(H)) - (tmpRight * Math.Sin(H));
tmpRight = Vector3.CrossProduct( tmpView, tmpUp);

// rotate around right vector (pitch)
tmpView = Vector3.Normalize(
(tmpView * Math.Cos(P)) + (tmpUp * Math.Sin(P));
tmpUp = -1 * Vector3.CrossProduct( tmpView, tmpRight)

// calculate cross/dot products
Vector3 cross = tmpUp.CrossProduct(upVector);
double  dot = tmpUp.DotProduct(upVector);

double angle = Math.Atan2( cross.Magnitude, dot);

return angle;

}

The problems I have with the above include: 1) Angle is only 'correct' for clockwise rotation (I thought the cross product was supposed to provide me with a direction.) 2) The Pitch and Heading are effecting the Roll angle. For instance when turning 90 degrees right or left from the initial heading, for every 1 degree of pitch up or down, the roll angle is being increased by two degrees. I am sure I have a fundamental problem with my approach, but after 3 days of banging my head against this, and searching google and these forums, i'm no closer to figuring out my mistake. Seems like the only answers I can find are limited to 2D, not 3D. Thanks for any suggestions.

##### Share on other sites
Ok, I spent the last few hours drawing diagrams trying to understand this better and changed my approach. I changed my the function for calculating the camera 'roll' compared to the horizon with the following:

public double GetRoll(){  double angle = Math.Acos( worldUpV.DotProduct(upVector));  if ( rightVector.Y > 0 ) { angle = -angle; }  return angle;}

However, this does not give me the expected result, once I start changing the pitch of the camera.

For instance, if I roll to 45 deg and pitch to 45 deg, I end up with a calculated angle of 60deg.

Or, if I just pitch the camera up, I get a calculated roll equivalent to my pitch. Which does makes sense to me, as pitching the camera up, moves the upVector away from the world Y axis.

Although pitching is the rotation around the rightVector, while I'm trying to find the rotation around the viewVector/viewDir.

Obviously missing something still - haha.

##### Share on other sites
Off the top of my head, here's something you might try. Note that this will only work when it's 'meaningful' to compute the roll at all (that is, the computation may fail if the object is headed straight up or down).

Here's what it might look like in pseudocode:
vector3 local_up = object.transform_vector_world_to_local(vector3(0,1,0));local_up.z = 0;local_up.normalize();float horizon_angle = atan2(local_up.y, local_up.x);
That may be totally wrong, but if you're still stuck on this, you might at least try it out and see what you get :)

##### Share on other sites
Thank you for your suggestion, but I'm not quite sure I understand how the approach is different than what I am currently trying.

Maybe its because I am misunderstanding your intention of the line:

vector3 local_up = object.transform_vector_world_to_local(vector3(0,1,0));

I am assuming that this would be the equivalent of taking the vector(0,1,0)
and applying the heading and pitch rotations.

Equivalent to what I did in my initial post with:
// get Heading and Pitchdouble H = GetAzimuth();double P = GetElevation();            // rotate around up vector (heading)tmpView = Vector3.Normalize((tmpView * Math.Cos(H)) - (tmpRight * Math.Sin(H)));tmpRight = Vector3.CrossProduct( tmpView, tmpUp);            // rotate around right vector (pitch)tmpView = Vector3.Normalize( (tmpView * Math.Cos(P)) + (tmpUp * Math.Sin(P)));tmpUp = -1 * Vector3.CrossProduct(tmpView, tmpRight);

But I think, that difference between what you intended with the 'world_to_local' function and what I do above, is that my 'tmpUp' vector is still described in world coordinates, while your 'local_up' would be described in 'local coordinates'.

Which means your 'local_up.Z = 0' has a different meaning than if I were to do 'tmpUp.Z = 0;'

This made me think that perhaps what I should do is to subtract the viewDir vector from the tmpUp vector (rather than zero) - but this didn't give the results I am looking for either...

Still confused - heh...

##### Share on other sites
pitch, yaw & roll are always tricky...

in my vector class, I use this function for pitch/yaw:

// x : LR, z : FB, y : UD
// angle 0, 0, 0 is (0, 0, 1) (straight into screen)
SVector3 SVector3::GetAnglesFromVector()
{
SVector3 vRet(x, y, z);
float fLength = GetLength();
float fForward;
float fYaw, fPitch;

vRet.Normalize();

if (vRet.z == 0 && vRet.x == 0)
{
fYaw = 0;
if (vRet.y > 0)
fPitch = 90.0f;
else
fPitch = 270.0f;
}
else
{
fYaw = (float) (atan2(vRet.x, vRet.z) * RADTODEG);
if (fYaw < 0)
fYaw += 360;

fForward = (float) (sqrt (vRet.x * vRet.x + vRet.z * vRet.z));
fPitch = (float) (atan2(vRet.y, fForward) * RADTODEG);
if (fPitch < 0.0f)
fPitch += 360.0f;
}

vRet.Set(fPitch, fYaw, 0.0f);

return vRet;
}

For the roll, I would take the side vector, Grab the angles and use the pitch

##### Share on other sites
BuffaloJ, thanks for your suggestion - I tried implementing my method using the approach you suggested - calculating the pitch of the right vector - it looked like it would work, then, I found the same problem when I pitched the camera up and down...

Here is what I implemented:
public double GetRoll(){  double rightLen = Math.Sqrt( (rightVector.X * rightVector.X) + (rightVector.Z * rightVector.Z));  double angle = Math.Atan2( rightVector.Y, rightLen);  if (upVector.Y > 0) { angle = -angle; }  return angle;}

The roll angle changes relative to the pitch angle in that, as the pitch angle approaches 90 degrees, the roll angle approaches zero.

If instance:
Pitch Roll   0    30  15    28.879  30    25.6589  45    20.7048  60    14.4775  75     7.4355  90     0

Which appears as if Roll angle is being multiplied by the Cosine of the pitch angle.

I really feel like I'm missing something completely obvious at this point...

##### Share on other sites
Quote:
 Original post by awdorrinThank you for your suggestion, but I'm not quite sure I understand how the approach is different than what I am currently trying.Maybe its because I am misunderstanding your intention of the line:vector3 local_up = object.transform_vector_world_to_local(vector3(0,1,0));
Try replacing the above line with something like this:
vector3 local_up;local_up.x = object.side.y;local_up.y = object.up.y;local_up.z = object.forward.y;
And see if that gets you any closer. (If not, post back, and I'll try to post a clearer explanation of what the pseudocode is supposed to do.)

##### Share on other sites
Actually... I think the last code I posted may be correct. The results I was seeing just were not making sense according to what I expected to see.

Thinking more about it, it does make sense that as I pitch the camera up towards a vertical of 90deg (or down towards -90 degrees) that the roll angle relative to the horizon would change, just in the manner I described (following a circular curve.)

If I am pointing straight up, the roll of the camera, relative to the ground (world X-Z plane) does not effect the 'horizon angle.'

I think the problem I am having in my program now, is not related to the roll angle, but to how I am using that angle to calculate the relative horizon line.
So time to go review that code to see if I can figure out my problems there. ;)

Thanks for the suggestions and pointers - think I might actually be past this mental block!

##### Share on other sites
After going over this for a few more days, I finally figured it out.

I was getting exactly what I was asking for, the roll relative to the world horizon, however using the Dot Product of the vectors was including the pitch in the angle, in addition to the roll relative to the view vector.

What I settled on was the following code:

public double GetRoll(){  Vector3 relright = viewDir.CrossProduct(worldUpV);  double relativeRoll = rightVector.DotProduct(relright);  double pitchComponent = Math.Cos(this.GetPitch());  double factor = relativeRoll/pitchComponent;  factor = Math.Max(-1, Math.Min(factor, 1));  if (rightVector.Y < 0)  {    return Math.Acos(factor);  }  else  {    return -Math.Acos(factor);  }}

I decided to use the camera viewDir vector and the worldUp vector to calculate a relative right vector and then take the dot product of the camera right vector and the relative right vector, so that the angles would be in the right quadrants.

This gave me the angle that was a combination of the roll and pitch, so I needed to remove the pitch component.

By dividing the dot product by the Cos(pitchAngle) I got the relative roll.

Then, in order to get a full -180 to 180 degrees, I used the direction of the right vector's Y component to negate the angle.

This is finally giving me what I needed to see.

There may be a much easier approach to do this, but for now this appears to do exactly what I was looking to do.

Figured I'd follow up in case any one else would find it useful.

(EDIT: had to make a change to correct for boundary conditions and possible division by zero)

[Edited by - awdorrin on February 12, 2010 12:21:19 PM]

• 10
• 18
• 14
• 19
• 15
• ### Similar Content

• By mmmax3d
Hi everyone,
I would need some assistance from anyone who has a similar experience
or a nice idea!
I have created a skybox (as cube) and now I need to add a floor/ground.
The skybox is created from cubemap and initially it was infinite.
Now it is finite with a specific size. The floor is a quad in the middle
of the skybox, like a horizon.
I have two problems:
When moving the skybox upwards or downwards, I need to
sample from points even above the horizon while sampling
from the botton at the same time.  I am trying to create a seamless blending of the texture
at the points of the horizon, when the quad is connected
to the skybox. However, I get skew effects. Does anybody has done sth similar?
Is there any good practice?
Thanks everyone!
• By mmmax3d
Hi everyone,
I would need some assistance from anyone who has a similar experience
or a nice idea!
I have created a skybox (as cube) and now I need to add a floor/ground.
The skybox is created from cubemap and initially it was infinite.
Now it is finite with a specific size. The floor is a quad in the middle
of the skybox, like a horizon.
I have two problems:
When moving the skybox upwards or downwards, I need to
sample from points even above the horizon while sampling
from the botton at the same time.  I am trying to create a seamless blending of the texture
at the points of the horizon, when the quad is connected
to the skybox. However, I get skew effects. Does anybody has done sth similar?
Is there any good practice?
Thanks everyone!

• I'm trying to implement PBR into my simple OpenGL renderer and trying to use multiple lighting passes, I'm using one pass per light for rendering as follow:
1- First pass = depth
2- Second pass = ambient
3- [3 .. n] for all the lights in the scene.
I'm using the blending function glBlendFunc(GL_ONE, GL_ONE) for passes [3..n], and i'm doing a Gamma Correction at the end of each fragment shader.
But i still have a problem with the output image it just looks noisy specially when i'm using texture maps.
Is there anything wrong with those steps or is there any improvement to this process?

• Hello Everyone!
I'm learning openGL, and currently i'm making a simple 2D game engine to test what I've learn so far.  In order to not say to much, i made a video in which i'm showing you the behavior of the rendering.
Video:

What i was expecting to happen, was the player moving around. When i render only the player, he moves as i would expect. When i add a second Sprite object, instead of the Player, this new sprite object is moving and finally if i add a third Sprite object the third one is moving. And the weird think is that i'm transforming the Vertices of the Player so why the transformation is being applied somewhere else?

Take a look at my code:
Sprite Class
(You mostly need to see the Constructor, the Render Method and the Move Method)
#include "Brain.h" #include <glm/gtc/matrix_transform.hpp> #include <vector> struct Sprite::Implementation { //Position. struct pos pos; //Tag. std::string tag; //Texture. Texture *texture; //Model matrix. glm::mat4 model; //Vertex Array Object. VertexArray *vao; //Vertex Buffer Object. VertexBuffer *vbo; //Layout. VertexBufferLayout *layout; //Index Buffer Object. IndexBuffer *ibo; //Shader. Shader *program; //Brains. std::vector<Brain *> brains; //Deconstructor. ~Implementation(); }; Sprite::Sprite(std::string image_path, std::string tag, float x, float y) { //Create Pointer To Implementaion. m_Impl = new Implementation(); //Set the Position of the Sprite object. m_Impl->pos.x = x; m_Impl->pos.y = y; //Set the tag. m_Impl->tag = tag; //Create The Texture. m_Impl->texture = new Texture(image_path); //Initialize the model Matrix. m_Impl->model = glm::mat4(1.0f); //Get the Width and the Height of the Texture. int width = m_Impl->texture->GetWidth(); int height = m_Impl->texture->GetHeight(); //Create the Verticies. float verticies[] = { //Positions //Texture Coordinates. x, y, 0.0f, 0.0f, x + width, y, 1.0f, 0.0f, x + width, y + height, 1.0f, 1.0f, x, y + height, 0.0f, 1.0f }; //Create the Indicies. unsigned int indicies[] = { 0, 1, 2, 2, 3, 0 }; //Create Vertex Array. m_Impl->vao = new VertexArray(); //Create the Vertex Buffer. m_Impl->vbo = new VertexBuffer((void *)verticies, sizeof(verticies)); //Create The Layout. m_Impl->layout = new VertexBufferLayout(); m_Impl->layout->PushFloat(2); m_Impl->layout->PushFloat(2); m_Impl->vao->AddBuffer(m_Impl->vbo, m_Impl->layout); //Create the Index Buffer. m_Impl->ibo = new IndexBuffer(indicies, 6); //Create the new shader. m_Impl->program = new Shader("Shaders/SpriteShader.shader"); } //Render. void Sprite::Render(Window * window) { //Create the projection Matrix based on the current window width and height. glm::mat4 proj = glm::ortho(0.0f, (float)window->GetWidth(), 0.0f, (float)window->GetHeight(), -1.0f, 1.0f); //Set the MVP Uniform. m_Impl->program->setUniformMat4f("u_MVP", proj * m_Impl->model); //Run All The Brains (Scripts) of this game object (sprite). for (unsigned int i = 0; i < m_Impl->brains.size(); i++) { //Get Current Brain. Brain *brain = m_Impl->brains[i]; //Call the start function only once! if (brain->GetStart()) { brain->SetStart(false); brain->Start(); } //Call the update function every frame. brain->Update(); } //Render. window->GetRenderer()->Draw(m_Impl->vao, m_Impl->ibo, m_Impl->texture, m_Impl->program); } void Sprite::Move(float speed, bool left, bool right, bool up, bool down) { if (left) { m_Impl->pos.x -= speed; m_Impl->model = glm::translate(m_Impl->model, glm::vec3(-speed, 0, 0)); } if (right) { m_Impl->pos.x += speed; m_Impl->model = glm::translate(m_Impl->model, glm::vec3(speed, 0, 0)); } if (up) { m_Impl->pos.y += speed; m_Impl->model = glm::translate(m_Impl->model, glm::vec3(0, speed, 0)); } if (down) { m_Impl->pos.y -= speed; m_Impl->model = glm::translate(m_Impl->model, glm::vec3(0, -speed, 0)); } } void Sprite::AddBrain(Brain * brain) { //Push back the brain object. m_Impl->brains.push_back(brain); } pos *Sprite::GetPos() { return &m_Impl->pos; } std::string Sprite::GetTag() { return m_Impl->tag; } int Sprite::GetWidth() { return m_Impl->texture->GetWidth(); } int Sprite::GetHeight() { return m_Impl->texture->GetHeight(); } Sprite::~Sprite() { delete m_Impl; } //Implementation Deconstructor. Sprite::Implementation::~Implementation() { delete texture; delete vao; delete vbo; delete layout; delete ibo; delete program; }
Renderer Class
#include "Renderer.h" #include "Error.h" Renderer::Renderer() { } Renderer::~Renderer() { } void Renderer::Draw(VertexArray * vao, IndexBuffer * ibo, Texture *texture, Shader * program) { vao->Bind(); ibo->Bind(); program->Bind(); if (texture != NULL) texture->Bind(); GLCall(glDrawElements(GL_TRIANGLES, ibo->GetCount(), GL_UNSIGNED_INT, NULL)); } void Renderer::Clear(float r, float g, float b) { GLCall(glClearColor(r, g, b, 1.0)); GLCall(glClear(GL_COLOR_BUFFER_BIT)); } void Renderer::Update(GLFWwindow *window) { /* Swap front and back buffers */ glfwSwapBuffers(window); /* Poll for and process events */ glfwPollEvents(); }
#shader vertex #version 330 core layout(location = 0) in vec4 aPos; layout(location = 1) in vec2 aTexCoord; out vec2 t_TexCoord; uniform mat4 u_MVP; void main() { gl_Position = u_MVP * aPos; t_TexCoord = aTexCoord; } #shader fragment #version 330 core out vec4 aColor; in vec2 t_TexCoord; uniform sampler2D u_Texture; void main() { aColor = texture(u_Texture, t_TexCoord); } Also i'm pretty sure that every time i'm hitting the up, down, left and right arrows on the keyboard, i'm changing the model Matrix of the Player and not the others.

Window Class:
#include "Window.h" #include <GL/glew.h> #include <GLFW/glfw3.h> #include "Error.h" #include "Renderer.h" #include "Scene.h" #include "Input.h" //Global Variables. int screen_width, screen_height; //On Window Resize. void OnWindowResize(GLFWwindow *window, int width, int height); //Implementation Structure. struct Window::Implementation { //GLFW Window. GLFWwindow *GLFW_window; //Renderer. Renderer *renderer; //Delta Time. double delta_time; //Frames Per Second. int fps; //Scene. Scene *scnene; //Input. Input *input; //Deconstructor. ~Implementation(); }; //Window Constructor. Window::Window(std::string title, int width, int height) { //Initializing width and height. screen_width = width; screen_height = height; //Create Pointer To Implementation. m_Impl = new Implementation(); //Try initializing GLFW. if (!glfwInit()) { std::cout << "GLFW could not be initialized!" << std::endl; std::cout << "Press ENTER to exit..." << std::endl; std::cin.get(); exit(-1); } //Setting up OpenGL Version 3.3 Core Profile. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); /* Create a windowed mode window and its OpenGL context */ m_Impl->GLFW_window = glfwCreateWindow(width, height, title.c_str(), NULL, NULL); if (!m_Impl->GLFW_window) { std::cout << "GLFW could not create a window!" << std::endl; std::cout << "Press ENTER to exit..." << std::endl; std::cin.get(); glfwTerminate(); exit(-1); } /* Make the window's context current */ glfwMakeContextCurrent(m_Impl->GLFW_window); //Initialize GLEW. if(glewInit() != GLEW_OK) { std::cout << "GLEW could not be initialized!" << std::endl; std::cout << "Press ENTER to exit..." << std::endl; std::cin.get(); glfwTerminate(); exit(-1); } //Enabling Blending. GLCall(glEnable(GL_BLEND)); GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); //Setting the ViewPort. GLCall(glViewport(0, 0, width, height)); //**********Initializing Implementation**********// m_Impl->renderer = new Renderer(); m_Impl->delta_time = 0.0; m_Impl->fps = 0; m_Impl->input = new Input(this); //**********Initializing Implementation**********// //Set Frame Buffer Size Callback. glfwSetFramebufferSizeCallback(m_Impl->GLFW_window, OnWindowResize); } //Window Deconstructor. Window::~Window() { delete m_Impl; } //Window Main Loop. void Window::MainLoop() { //Time Variables. double start_time = 0, end_time = 0, old_time = 0, total_time = 0; //Frames Counter. int frames = 0; /* Loop until the user closes the window */ while (!glfwWindowShouldClose(m_Impl->GLFW_window)) { old_time = start_time; //Total time of previous frame. start_time = glfwGetTime(); //Current frame start time. //Calculate the Delta Time. m_Impl->delta_time = start_time - old_time; //Get Frames Per Second. if (total_time >= 1) { m_Impl->fps = frames; total_time = 0; frames = 0; } //Clearing The Screen. m_Impl->renderer->Clear(0, 0, 0); //Render The Scene. if (m_Impl->scnene != NULL) m_Impl->scnene->Render(this); //Updating the Screen. m_Impl->renderer->Update(m_Impl->GLFW_window); //Increasing frames counter. frames++; //End Time. end_time = glfwGetTime(); //Total time after the frame completed. total_time += end_time - start_time; } //Terminate GLFW. glfwTerminate(); } //Load Scene. void Window::LoadScene(Scene * scene) { //Set the scene. m_Impl->scnene = scene; } //Get Delta Time. double Window::GetDeltaTime() { return m_Impl->delta_time; } //Get FPS. int Window::GetFPS() { return m_Impl->fps; } //Get Width. int Window::GetWidth() { return screen_width; } //Get Height. int Window::GetHeight() { return screen_height; } //Get Input. Input * Window::GetInput() { return m_Impl->input; } Renderer * Window::GetRenderer() { return m_Impl->renderer; } GLFWwindow * Window::GetGLFWindow() { return m_Impl->GLFW_window; } //Implementation Deconstructor. Window::Implementation::~Implementation() { delete renderer; delete input; } //OnWindowResize void OnWindowResize(GLFWwindow *window, int width, int height) { screen_width = width; screen_height = height; //Updating the ViewPort. GLCall(glViewport(0, 0, width, height)); }
Brain Class
#include "Brain.h" #include "Sprite.h" #include "Window.h" struct Brain::Implementation { //Just A Flag. bool started; //Window Pointer. Window *window; //Sprite Pointer. Sprite *sprite; }; Brain::Brain(Window *window, Sprite *sprite) { //Create Pointer To Implementation. m_Impl = new Implementation(); //Initialize Implementation. m_Impl->started = true; m_Impl->window = window; m_Impl->sprite = sprite; } Brain::~Brain() { //Delete Pointer To Implementation. delete m_Impl; } void Brain::Start() { } void Brain::Update() { } Window * Brain::GetWindow() { return m_Impl->window; } Sprite * Brain::GetSprite() { return m_Impl->sprite; } bool Brain::GetStart() { return m_Impl->started; } void Brain::SetStart(bool value) { m_Impl->started = value; } Script Class (Its a Brain Subclass!!!)
#include "Script.h" Script::Script(Window *window, Sprite *sprite) : Brain(window, sprite) { } Script::~Script() { } void Script::Start() { std::cout << "Game Started!" << std::endl; } void Script::Update() { Input *input = this->GetWindow()->GetInput(); Sprite *sp = this->GetSprite(); //Move this sprite. this->GetSprite()->Move(200 * this->GetWindow()->GetDeltaTime(), input->GetKeyDown("left"), input->GetKeyDown("right"), input->GetKeyDown("up"), input->GetKeyDown("down")); std::cout << sp->GetTag().c_str() << ".x = " << sp->GetPos()->x << ", " << sp->GetTag().c_str() << ".y = " << sp->GetPos()->y << std::endl; }
Main:
#include "SpaceShooterEngine.h" #include "Script.h" int main() { Window w("title", 600,600); Scene *scene = new Scene(); Sprite *player = new Sprite("Resources/Images/player.png", "Player", 100,100); Sprite *other = new Sprite("Resources/Images/cherno.png", "Other", 400, 100); Sprite *other2 = new Sprite("Resources/Images/cherno.png", "Other", 300, 400); Brain *brain = new Script(&w, player); player->AddBrain(brain); scene->AddSprite(player); scene->AddSprite(other); scene->AddSprite(other2); w.LoadScene(scene); w.MainLoop(); return 0; }

I literally can't find what is wrong. If you need more code, ask me to post it. I will also attach all the source files.
Brain.cpp
Error.cpp
IndexBuffer.cpp
Input.cpp
Renderer.cpp
Scene.cpp
Sprite.cpp
Texture.cpp
VertexArray.cpp
VertexBuffer.cpp
VertexBufferLayout.cpp
Window.cpp
Brain.h
Error.h
IndexBuffer.h
Input.h
Renderer.h
Scene.h
SpaceShooterEngine.h
Sprite.h
Texture.h
VertexArray.h
VertexBuffer.h
VertexBufferLayout.h
Window.h

• Hello fellow programmers,
For a couple of days now i've decided to build my own planet renderer just to see how floating point precision issues
can be tackled. As you probably imagine, i've quickly faced FPP issues when trying to render absurdly large planets.

I have used the classical quadtree LOD approach;
I've generated my grids with 33 vertices, (x: -1 to 1, y: -1 to 1, z = 0).
Each grid is managed by a TerrainNode class that, depending on the side it represents (top, bottom, left right, front, back),
creates a special rotation-translation matrix that moves and rotates the grid away from the origin so that when i finally
normalize all the vertices on my vertex shader i can get a perfect sphere.
T = glm::translate(glm::dmat4(1.0), glm::dvec3(0.0, 0.0, 1.0)); R = glm::rotate(glm::dmat4(1.0), glm::radians(180.0), glm::dvec3(1.0, 0.0, 0.0)); sides[0] = new TerrainNode(1.0, radius, T * R, glm::dvec2(0.0, 0.0), new TerrainTile(1.0, SIDE_FRONT)); T = glm::translate(glm::dmat4(1.0), glm::dvec3(0.0, 0.0, -1.0)); R = glm::rotate(glm::dmat4(1.0), glm::radians(0.0), glm::dvec3(1.0, 0.0, 0.0)); sides[1] = new TerrainNode(1.0, radius, R * T, glm::dvec2(0.0, 0.0), new TerrainTile(1.0, SIDE_BACK)); // So on and so forth for the rest of the sides As you can see, for the front side grid, i rotate it 180 degrees to make it face the camera and push it towards the eye;
the back side is handled almost the same way only that i don't need to rotate it but simply push it away from the eye.
The same technique is applied for the rest of the faces (obviously, with the proper rotations / translations).
The matrix that result from the multiplication of R and T (in that particular order) is send to my vertex shader as r_Grid'.
// spherify vec3 V = normalize((r_Grid * vec4(r_Vertex, 1.0)).xyz); gl_Position = r_ModelViewProjection * vec4(V, 1.0); The r_ModelViewProjection' matrix is generated on the CPU in this manner.
// No the most efficient way, but it works. glm::dmat4 Camera::getMatrix() { // Create the view matrix // Roll, Yaw and Pitch are all quaternions. glm::dmat4 View = glm::toMat4(Roll) * glm::toMat4(Pitch) * glm::toMat4(Yaw); // The model matrix is generated by translating in the oposite direction of the camera. glm::dmat4 Model = glm::translate(glm::dmat4(1.0), -Position); // Projection = glm::perspective(fovY, aspect, zNear, zFar); // zNear = 0.1, zFar = 1.0995116e12 return Projection * View * Model; } I managed to get rid of z-fighting by using a technique called Logarithmic Depth Buffer described in this article; it works amazingly well, no z-fighting at all, at least not visible.
Each frame i'm rendering each node by sending the generated matrices this way.
// set the r_ModelViewProjection uniform // Sneak in the mRadiusMatrix which is a matrix that contains the radius of my planet. Shader::setUniform(0, Camera::getInstance()->getMatrix() * mRadiusMatrix); // set the r_Grid matrix uniform i created earlier. Shader::setUniform(1, r_Grid); grid->render(); My planet's radius is around 6400000.0 units, absurdly large, but that's what i really want to achieve;
Everything works well, the node's split and merge as you'd expect, however whenever i get close to the surface
of the planet the rounding errors start to kick in giving me that lovely stairs effect.
I've read that if i could render each grid relative to the camera i could get better precision on the surface, effectively
getting rid of those rounding errors.

My question is how can i achieve this relative to camera rendering in my scenario here?
I know that i have to do most of the work on the CPU with double, and that's exactly what i'm doing.
I only use double on the CPU side where i also do most of the matrix multiplications.
As you can see from my vertex shader i only do the usual r_ModelViewProjection * (some vertex coords).