0

I am rendering 3d scene with OpenGL. Users are allowed to orbit the scene. Besides the objects in the scene, I want to draw a axis indicator at the top right or bottom left corner to show the current roation status.

Something like the viewport widget at the top right corner in Blender.

Can anyone tell me the direction to do it?

Jie Chen
  • 53
  • 4

2 Answers2

-1

The way I would do it is to: First- Render your scene; Second- Render the axis indicator (view cube) on its own to a texture attachment in a frame buffer. Third- Render the resulting texture to a quad in the top-right of your screen. Something similar to this: https://learnopengl.com/Advanced-OpenGL/Framebuffers

jake
  • 43
  • 4
-1

Very briefly explained, once you have a 3D axis, you start by drawing the 3D axis at camera.Position + camera.Front * smallDist, so it always draws at the center of the screen.

Then, you use glViewport(...) to set the viewport to the corner of the screen, and draw the axis. You can also use eg. glLineWidth(3.0f) to make the lines stand out more.

Here's a rough idea:

#include <iostream>
#include <vector>
#include <math.h>
 
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/ext.hpp>
// camera.h is from basic camera learnopengl tutorial
#include "camera.h"
 
using glm::mat4;
using glm::vec3;
using glm::radians;
using glm::lookAt;
using std::vector;
 
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
 
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
 
// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;

Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

class Line {
    int shaderProgram;
    unsigned int VBO, VAO;
    vector<float> vertices;
    vec3 startPoint;
    vec3 endPoint;
    mat4 MVP = mat4(1.0);
    vec3 lineColor;
public:
    Line(vec3 start, vec3 end) {
 
        startPoint = start;
        endPoint = end;
        lineColor = vec3(1,1,1);
 
        const char *vertexShaderSource = "#version 330 core\n"
            "layout (location = 0) in vec3 aPos;\n"
            "uniform mat4 MVP;\n"
            "void main()\n"
            "{\n"
            "   gl_Position = MVP * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
            "}\0";
        const char *fragmentShaderSource = "#version 330 core\n"
            "out vec4 FragColor;\n"
            "uniform vec3 color;\n"
            "void main()\n"
            "{\n"
            "   FragColor = vec4(color, 1.0f);\n"
            "}\n\0";
 
        // vertex shader
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        // check for shader compile errors
 
        // fragment shader
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        // check for shader compile errors
 
        // link shaders
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        // check for linking errors
 
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
 
        vertices = {
             start.x, start.y, start.z,
             end.x, end.y, end.z,
 
        };
        
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glBindVertexArray(VAO);
 
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices)*vertices.size(), vertices.data(), GL_STATIC_DRAW);
 
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
 
        glBindBuffer(GL_ARRAY_BUFFER, 0); 
        glBindVertexArray(0); 
 
    }
 
    int setMVP(mat4 mvp) {
        MVP = mvp;
        return 1;
    }
 
    int setColor(vec3 color) {
        lineColor = color;
        return 1;
    }
 
    int draw() {
        glUseProgram(shaderProgram);
 
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "MVP"), 1, GL_FALSE, &MVP[0][0]);
        glUniform3fv(glGetUniformLocation(shaderProgram, "color"), 1, &lineColor[0]);
 
        glBindVertexArray(VAO);
        glDrawArrays(GL_LINES, 0, 2);
        return 1;
    }
 
    ~Line() {
        glDeleteVertexArrays(1, &VAO);
        glDeleteBuffers(1, &VBO);
        glDeleteProgram(shaderProgram);
    }
};
 
int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
 
    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "drawing lines", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);  
    glfwMakeContextCurrent(window);
    glfwSetCursorPosCallback(window, mouse_callback);

 
    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    camera.Position = vec3(0,0,3);
    camera.MovementSpeed = 0.01f;

    // 3d lines example
    Line line1(vec3(0,0,0), vec3(0.04,0,0));
    line1.setColor(vec3(1,0,0));
    Line line2(vec3(0,0,0), vec3(0,0.04,0));
    line2.setColor(vec3(0,1,0));
    Line line3(vec3(0,0,0), vec3(0,0,0.04));
    line3.setColor(vec3(0,0,1));
    Line line4(vec3(0,0,0), vec3(10,0,0));
    line4.setColor(vec3(1,0,0));
    Line line5(vec3(0,0,0), vec3(0,10,0));
    line5.setColor(vec3(0,1,0));
    Line line6(vec3(0,0,0), vec3(0,0,10));
    line6.setColor(vec3(0,0,1));

    glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float) SCR_WIDTH / (float)SCR_HEIGHT, 0.01f, 1000.0f);

 
    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        // input
        // -----
        processInput(window);
 
        // render
        // ------
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);

        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.01f, 1000.0f);
        glm::mat4 view = camera.GetViewMatrix();

        float ar = (float)SCR_WIDTH / (float)SCR_HEIGHT;
        float fov = 45.0f;
        float nearDist = 0.01f;
        float Hnear = 2.0f * tan(glm::radians(fov/2)) * nearDist;
        float Wnear = Hnear * ar;
        glm::vec3 axisPosition = camera.Position + glm::normalize(camera.Front) * 0.2f;
            
        line1.setMVP(projection * view * glm::translate(glm::mat4(1.0f), axisPosition));
        line2.setMVP(projection * view * glm::translate(glm::mat4(1.0f), axisPosition));
        line3.setMVP(projection * view * glm::translate(glm::mat4(1.0f), axisPosition));
 
        glViewport(SCR_WIDTH-SCR_WIDTH/10.0f,SCR_HEIGHT-SCR_HEIGHT/10.0f,SCR_WIDTH/10.0f,SCR_HEIGHT/10.0f);
        glLineWidth(3.0f);
        line1.draw();
        line2.draw();
        line3.draw();
        glLineWidth(1.0f);
        glViewport(0,0,SCR_WIDTH, SCR_HEIGHT);

        line4.setMVP(projection * view);
        line5.setMVP(projection * view);
        line6.setMVP(projection * view);
 
        line4.draw();
        line5.draw();
        line6.draw();
  
        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
 
 
    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}
 
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

Result:

small 3d axis

jackw11111
  • 1,457
  • 1
  • 17
  • 34