# Camera in OpenGL

Cameras in OpenGL:

There is no concept of camera in OpenGL. It simulates the camera by moving all objects in the scene in the opposite direction. To define a camera, you need its position in world space, the direction of observation, a vector to its right and a vector to its top. In the discussion of camera coordinates (right-hand coordinate system), the camera's perspective is taken as the origin of the scene.

Don't forget that the positive z axis is pointing at you from the screen. If you want the camera to move backward, move along the positive z axis.

The Direction Vector is not the best name because it actually points in the opposite direction from it to the target vector (note that in the previous picture, the blue direction vector probably points in the positive direction of the z axis, which is exactly opposite to the direction the camera actually points)

Camera direction vector:

It originally refers to the direction the camera points to, but considering that the camera points to the negative Z-axis direction (cameratarget camerapos, the direction vector is defined as the positive Z-axis direction pointing to the camera (camerapos cameratarget).

Right vector and up axis of camera:

The right vector represents the positive direction of the X-axis of the camera space, which can be obtained by defining a cross product of the up vector and the direction vector and normalizing it.

glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);

The up axis is obtained by cross multiplication of direction vector and right vector

LookAt :

Just define a camera position, a target position and a vector representing the up vector in world space (the up vector used to calculate the right vector).

view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);

Where cameraFront is the negative direction pointing to the Z axis, and the direction is the current position plus the direction vector we just defined. This ensures that no matter how we move, the camera will look at the target direction.

Control camera movement with cursor:

void processInput(GLFWwindow *window)
{
float cameraSpeed = 0.05f; // adjust accordingly
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
cameraPos += cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
cameraPos -= cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

Movement speed:

In fact, depending on the capabilities of the processor, some people may draw more frames per second than others, that is, call the processInput function at a higher frequency. As a result, depending on the configuration, some people may move very fast, while others may move very slowly. By defining a time difference to store all the time of rendering the previous frame, you can multiply the speed by the time difference. If the delta time is large, it means that the rendering of the previous frame takes more time, so the speed of this frame needs to be higher to balance the time spent in rendering.

Angle movement:

Any rotation in space can be expressed by Euler angle, including pitch angle, yaw angle and roll angle.

As shown in the figure below:

Direction vector based on pitch angle and yaw angle:

direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw)); / / Note: direction represents the front of the camera, which is the opposite of the direction vector of the second camera of the first picture in this paper

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
TexCoord = vec2(aTexCoord.x,1- aTexCoord.y);
}

#version 330 core
out vec4 FragColor;

in vec2 TexCoord;
uniform float mixValue;

// Sampler: solves how to pass texture objects to clip shaders
//Texture unit·
uniform sampler2D texture1;
uniform sampler2D texture2;

//The output of this fragment shader is the color (filtered) on the texture's (interpolated) texture coordinates.
void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2( TexCoord.x, 1 - TexCoord.y)), 0.5f);

}

Main program:

#define STB_IMAGE_IMPLEMENTATION
#include"STB_IMAGE/stb_image.h"
#include<GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <Texture.h>
#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
const unsigned int WINDOW_WIDTH = 800;
const unsigned int WINDOW_HEIGHT =600;

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);

float lastTime = 0.0f;
float deltaTime = 0.0f;

bool firstMouse = true;
double lastX = WINDOW_WIDTH / 2;
double lastY = WINDOW_HEIGHT / 2;
float yaw = -90.0f;	// yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing to the right so we initially rotate a bit to the left.
float pitch = 0.0f;
float fov = 45.0f;

int  main()
{
//1 instantiate GLFW window
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

//Create a window object
GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "TestWindows", NULL, NULL);
if (!window)
{
std::cout << "Failed to create Windows" << std::endl;
//Release resources
glfwTerminate();
return -1;

}
//Keep context
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}

//vertex data
float vertexDatas[] = {
//----Location color texture coordinates-
-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
};

unsigned int indices[] = { // Note that the index starts from 0!
0, 1, 3, // First triangle
1, 2, 3  // Second triangle
};

//2 create VAO,VBO,EBO
unsigned int VAOID, VBOId, EBOID;
glGenVertexArrays(1, &VAOID);  //The first parameter is the number of objects
glGenBuffers(1, &VBOId);
glGenBuffers(1, &EBOID);

//Binding VAO
glBindVertexArray(VAOID);

//Bind to vertex buffer object
glBindBuffer(GL_ARRAY_BUFFER, VBOId);
//copy vertex data to buffer memory
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexDatas), vertexDatas, GL_STATIC_COPY);

//Parse vertex data and assign data to the vertex attribute array corresponding to 1
//The first parameter is the vertex attribute, as opposed to the shader program location
//Positional attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
//Enable vertex attributes, disabled by default//
glEnableVertexAttribArray(0);  //Parameters: vertex attribute values

//Texture attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

//Unbind VBO,VAO, do not need to unbind EBO (EBO cannot unbind when VAO is bound
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind VBO, the second parameter is to unbind and return to the original value
glBindVertexArray(0); // The first parameter of unbind VAO is to unbind and return to the original value

//4. Texture object
unsigned int textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);

//Set texture wrapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//Texture sampling method
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

int width, height, channels;

unsigned char* data = stbi_load("F:/C++project/LibsInclude/src/face.png", &width, &height, &channels, 0);
if (data)
{
//Load the data of texture object and associate the data in memory with texture object
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load Firsttexture" << std::endl;
}

//Free memory
stbi_image_free(data);

unsigned int textureId2;
glGenTextures(1, &textureId2);
glBindTexture(GL_TEXTURE_2D, textureId2);

//Set texture wrapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//Texture sampling method
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

data = stbi_load("F:/C++project/LibsInclude/src/container.jpg", &width, &height, &channels, 0);
if (data)
{
//Load the data of texture object and associate the data in memory with texture object
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load Firsttexture" << std::endl;
}

//Free memory
stbi_image_free(data);
//Index texture cells

//Opening depth test
glEnable(GL_DEPTH_TEST);

//Circular rendering
while (!glfwWindowShouldClose(window))
{

float currentTime = glfwGetTime();
deltaTime = currentTime - lastTime;
lastTime = currentTime;
//input
processInput(window);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
//Clear color and depth buffers
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//Binding texture units and texture objects
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureId2);

glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::mat4(1.0f);
model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));

view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
projection = glm::perspective(glm::radians(fov), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 100.0f);

glBindVertexArray(VAOID);
glDrawArrays(GL_TRIANGLES, 0, 36);

//Swap color buffer
glfwSwapBuffers(window);
//Check whether the event is triggered
glfwPollEvents();
}

//Delete VBO, VAO
glDeleteVertexArrays(1, &VAOID);
glDeleteBuffers(1, &VBOId);

//Delete program object

//Release resources
glfwTerminate();
return 0;

}

//Callback function, the viewport changes when the window changes
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height );
}

//Input control
void processInput(GLFWwindow* window)
{
float moveSpeed = 0.5f * deltaTime;
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
{
cameraPos += cameraFront * moveSpeed;
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
{
cameraPos -= cameraFront * moveSpeed;
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
{
cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp))* moveSpeed;
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
{
cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp))* moveSpeed;
}
}

//Mouse callback
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;
lastX = xpos;
lastY = ypos;

float sensitivity = 0.05;
xoffset *= sensitivity;
yoffset *= sensitivity;

yaw += xoffset;
pitch += yoffset;

if (pitch > 89.0f)
pitch = 89.0f;
if (pitch < -89.0f)
pitch = -89.0f;

glm::vec3 front;
cameraFront = glm::normalize(front);
}

//Define cursor callback function
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
if (fov >= 1.0f && fov <= 90.0f)
fov -= yoffset;
if (fov <= 1.0f)
fov = 1.0f;
if (fov >= 90.0f)
fov = 90.0f;
}

Result:

Posted on Tue, 24 Mar 2020 10:06:41 -0400 by amelhedi