Axis Rotation – Basis Vectors | Scaling | Span

All points in space can be reached by scaling any set of basis vectors, but what the heck does that even mean? The resulting vector in 2D space for example, is a straight line from the tail of one basis vector to the tip of the other…

By scaling both of the basis vectors different amounts, the resulting vector is then able to point to every point in space, which bizarrely, can seem both simple and perplexing at the same time. There are some nice animations in the video that show you how basis vectors sweep out all points in space.

Personally, I don’t worry too much about using the correct terms when referring to things such as: unit vectors, span, subspace, etc – It’s all about geometry, and thinking about the geometric relationships via visualisation is the key to comprehension, although memorising the language can help I must admit.

Source code: C++ from... main.cpp

#include <glad/glad.h>
#include <GLFW/glfw3.h>
 
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
 
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
 
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
 
#include <vector>
#include <iostream>
#include <fstream>
 
#include "shader_configure.h"
#include "load_meshes_binary.h"
 
int main()
{
	// (1) GLFW: Initialise & Configure
	// -----------------------------------------
	if (!glfwInit())
		exit(EXIT_FAILURE);
 
	glfwWindowHint(GLFW_SAMPLES, 4); // Anti-aliasing.
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
	glfwWindowHint(GLFW_OPENGL_PROFILEGLFW_OPENGL_CORE_PROFILE);
	
	const GLFWvidmodemode = glfwGetVideoMode(glfwGetPrimaryMonitor());
 
	int monitor_width = mode->width;
	int monitor_height = mode->height;
 
	int window_width = (int)(monitor_width * 0.75f);
	int window_height = (int)(monitor_height * 0.75f);
 
	GLFWwindowwindow = glfwCreateWindow(window_widthwindow_height"Linear Algebra - Transformations Calculations"NULLNULL);
	// GLFWwindow* window = glfwCreateWindow(window_width, window_height, "Linear Algebra - Transformations Calculations", glfwGetPrimaryMonitor(), NULL); // Full Screen Mode ("Alt" + "F4" to Exit!)
 
	if (!window)
	{
		glfwTerminate();
		exit(EXIT_FAILURE);
	}
	glfwMakeContextCurrent(window);
	glfwSetWindowPos(window, (monitor_width - window_width) / 2, (monitor_height - window_height) / 2);
 
	glfwSwapInterval(1); // Set VSync rate 1:1 with monitor's refresh rate.
 
	// (2) GLAD: Load OpenGL Function Pointers
	// -------------------------------------------------------
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		glfwTerminate();
		exit(EXIT_FAILURE);
	}
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_MULTISAMPLE);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHAGL_ONE_MINUS_SRC_ALPHA);	
 
	// (3) Compile Shaders Read from Text Files
	// ------------------------------------------------------
	const charvert_shader = "../../Shaders/shader_glsl.vert";
	const charfrag_shader = "../../Shaders/shader_glsl.frag";
 
	Shader main_shader(vert_shaderfrag_shader);
	main_shader.use();	
	
	unsigned int view_matrix_loc = glGetUniformLocation(main_shader.ID, "view");
	unsigned int projection_matrix_loc = glGetUniformLocation(main_shader.ID, "projection");
	unsigned int camera_position_loc = glGetUniformLocation(main_shader.ID, "camera_position");
	
	glm::vec3 camera_position(100.0f, 0.0f, 0.0f); // -Z is into the screen.		
	glm::vec3 camera_target(0.0f, 0.0f, 0.0f);
	glm::vec3 camera_up(0.0f, 1.0f, 0.0f);
 
	glUniform3f(camera_position_loccamera_position.x, camera_position.y, camera_position.z);
 
	glm::mat4 view = glm::lookAt(camera_positioncamera_targetcamera_up);
	glUniformMatrix4fv(view_matrix_loc, 1, GL_FALSE, glm::value_ptr(view));
 
	glm::mat4 projection = glm::perspective(glm::radians(55.0f), (float)window_width / (float)window_height, 1.0f, 500.0f);
	glUniformMatrix4fv(projection_matrix_loc, 1, GL_FALSE, glm::value_ptr(projection));
	
	Model transformations_axes("Object & Material Files\\transformations_axes.obj"main_shader, 0);
	unsigned int cosine_sine_loc = glGetUniformLocation(main_shader.ID, "cosine_sine");
 
	float angle = 0.0f;
	int direction = 1;
 
	while (!glfwWindowShouldClose(window)) // Main-Loop
	{			
		// Rotating around the X axis
		// ----------------------------------
		angle += 0.25f * direction;
		float angleA = glm::radians(angle);
 
		if (angle <= 0.0f || angle >= 33.0f)
		{
			direction = -direction;
			std::cout << "\n\n ********************   Direction changed";
		}		
		glm::vec2 axesZY(glm::cos(angleA), glm::sin(angleA));
		glUniform2fv(cosine_sine_loc, 1, glm::value_ptr(axesZY));
			
		glm::vec3 white_vertex(0.0f, 23.32f, 29.97f);
			
		float Z = (glm::cos(angleA)) * white_vertex.z + (glm::sin(angleA)) * white_vertex.y;			
		float Y = - (glm::sin(angleA)) * white_vertex.z + (glm::cos(angleA)) * white_vertex.y;
 
		std::cout << "\n\n   Manually Around Axis X..... Z: " << Z << " --- Y: " << Y;	
 
		// -----------------------------------------------------------		
 
		glm::mat4 rotate_X(1.0f);
		rotate_X = glm::rotate(rotate_XangleA, glm::vec3(1, 0, 0));
 
		white_vertex = rotate_X * glm::vec4(white_vertex, 0.0f);
		std::cout << "\n   GLM Around Axis X..... Z: " << white_vertex.z << " --- Y: " << white_vertex.y;
 
		// -----------------------------------------------------------
 
		glClearColor(0.70f, 0.65f, 0.75f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		
		transformations_axes.process_draw_calls();
		
		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	glDeleteProgram(main_shader.ID);	
	glfwTerminate();
	exit(EXIT_SUCCESS);
}

Source code: C++ from... shader_configure.h & load_meshes_binary.h

These two source-code header files are quite long, and therefore will further distract attention away from the relevant key few lines of code required as highlighted in the video. click here to acquire the shader configure file & load meshes file if you're interested in seeing/compiling the complete source code.


Source code: GLSL from... shader_glsl.vert (Vertex shader)

#version 420 core
 
layout (location = 0) in vec3 aPos;	
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoord;
layout (location = 3) in unsigned int aMeshNum;
layout (location = 4) in unsigned int aSamplerPos;
 
out vec3 vert_pos_transformed; // Transformed vertex position coordinates also passed as interpolated.
out vec2 texture_coordinates;
out vec3 vertex_normal;
flat out unsigned int mesh_number; // Can be used to identify and transform meshes independently of one another.
flat out unsigned int sampler_array_pos;
 
uniform int rendering_multiple_meshes;
 
uniform mat4 view;
uniform mat4 projection;
 
uniform vec2 cosine_sine;
 
void main()
{
	if (rendering_multiple_meshes == -1)
		mesh_number = aMeshNum; // Receive mesh number via input attribute.
	else
		mesh_number = rendering_multiple_meshes; // For draw method option 0... Receive mesh number via uniform.		
	
	float Z = (cosine_sine[0] * aPos.z)  +  (cosine_sine[1] * aPos.y);
	float Y = - (cosine_sine[1] * aPos.z)  +  (cosine_sine[0] * aPos.y);
	
	vec3 pos = aPos;
 
	if (mesh_number == 4 || mesh_number == 11) // 4 = Yellow vector... 11 = White vertex initial position.
		pos = vec3(aPos.x, Y, Z);
 
	texture_coordinates = aTexCoord;	
	sampler_array_pos = aSamplerPos;
	vert_pos_transformed = pos;
	
	vertex_normal = normalize(aNormal); // Not bothering to rotate the normals of the animated vector. 
 
	gl_Position = projection * view * vec4(pos, 1.0);
}

Source code: GLSL from... shader_glsl.frag (Fragment shader)

#version 420 core
 
out vec4 fragment_colour;
 
in vec3 vert_pos_transformed; // Transformed vertex position coordinates received as interpolated.
in vec2 texture_coordinates;
in vec3 vertex_normal;
flat in unsigned int mesh_number;
flat in unsigned int sampler_array_pos; // Used to select the correct image from the images[] array.
 
uniform bool meshes_combined;
uniform sampler2D images[32]; // Array of sampler images.
 
uniform vec3 camera_position; // -Z is into the screen... camera_position is set in main() on CPU side.
 
void main()
{	
	unsigned int index = 0;
	// --------------------------
	if (meshes_combined)
		index = sampler_array_pos;
 
	vec3 view_direction = normalize(camera_position - vert_pos_transformed);
 
	vec3 light_position = vec3(130.0, 35.0, -20.0);
	vec3 light_direction = normalize(vec3(light_position - vert_pos_transformed));	
	
	vec4 image_colour = texture(images[index], texture_coordinates);
 
	float ambient_factor = 0.35;
	vec4 ambient_result = vec4(ambient_factor * image_colour.rgb, 1.0);
 
	float diffuse_factor = 0.65;
	float diffuse_angle = max(dot(light_direction, vertex_normal), -0.0); // [-1.0 to 0] down to -1 results in darker lighting past 90 degrees.
	vec4 diffuse_result =  vec4(diffuse_factor * diffuse_angle * image_colour.rgb, 1.0);	
		
	vec3 specular_colour = vec3(0.35, 0.35, 0.35);
	vec3 reflect_direction = normalize(reflect(-light_direction, vertex_normal)); // Light direction is negated here.
	float specular_strength = pow(max(dot(view_direction, reflect_direction), 0), 16);
	vec4 specular_result = vec4(specular_colour * specular_strength, 1.0);
 
	fragment_colour = ambient_result + diffuse_result + specular_result;
 
	if (mesh_number == 19 || mesh_number == 20 || mesh_number == 21)
		fragment_colour = vec4(0.87, 0.92, 0.1, 1.0);
}