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); 
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));

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

glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);

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
direction.y = sin(glm::radians(pitch));
direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));

Clip shader:

#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);
}

Fragment Shader

#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<glad/glad.h>
#include<GLFW/glfw3.h>

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

#include <ShaderTest.h>
#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);

	//Initialize GLAD
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		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



	//3. Create shader
	ShaderTest shaderTest("vertexshader_Shader.vs", "fragementShader_Shader.fs");

	//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);

	//Load picture information
	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);
		//Set multi-level fade mode
		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);

	//Load picture information
	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);
		//Set multi-level fade mode
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
		std::cout << "Failed to load Firsttexture" << std::endl;
	}

	//Free memory
	stbi_image_free(data);
	//Index texture cells
	shaderTest.use();
	glUniform1i(glGetUniformLocation(shaderTest.ID, "texture1"), 0);
	glUniform1i(glGetUniformLocation(shaderTest.ID, "texture2"), 1);

	//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);
	
		//Activate shader program
		shaderTest.use();


		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);
		glUniformMatrix4fv(glGetUniformLocation(shaderTest.ID, "model"), 1, GL_FALSE, glm::value_ptr(model));
		glUniformMatrix4fv(glGetUniformLocation(shaderTest.ID, "view"), 1, GL_FALSE, &view[0][0]);
		glUniformMatrix4fv(glGetUniformLocation(shaderTest.ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection));

		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
	shaderTest.deleteProgram();

	//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;
	front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
	front.y = sin(glm::radians(pitch));
	front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
	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:

Tags: Programming Attribute Fragment Windows

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