12.Euler Angles & Homogeneous Coordinates

This commit is contained in:
hoenking 2025-12-14 16:29:58 +08:00
parent 0bf2e20d96
commit 69efacc681
14 changed files with 136 additions and 248 deletions

View File

@ -11,6 +11,20 @@
"cppStandard": "gnu++17", "cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64", "intelliSenseMode": "linux-gcc-x64",
"configurationProvider": "ms-vscode.makefile-tools" "configurationProvider": "ms-vscode.makefile-tools"
},
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"/Users/wanghao/VulkanSDK/1.4.328.1/macOS/include",
"/opt/local/include",
"/usr/local/include"
],
"defines": [],
"compilerPath": "/usr/bin/clang++",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "macos-clang-arm64"
} }
], ],
"version": 4 "version": 4

View File

@ -1,4 +1,5 @@
VULKAN_SDK_PATH = /Users/wanghao/VulkanSDK/1.4.328.1/macOS VULKAN_SDK_PATH = /Users/wanghao/VulkanSDK/1.4.328.1/macOS
GLSLC = $(VULKAN_SDK_PATH)/bin/glslc
CFLAGS = -std=c++17 -o2 -I. -I$(VULKAN_SDK_PATH)/include -I/opt/local/include CFLAGS = -std=c++17 -o2 -I. -I$(VULKAN_SDK_PATH)/include -I/opt/local/include
LDFLAGS = -L$(VULKAN_SDK_PATH)/lib -L/opt/local/lib `pkg-config --static --libs glfw3` -lvulkan -ldl -lpthread -lX11 LDFLAGS = -L$(VULKAN_SDK_PATH)/lib -L/opt/local/lib `pkg-config --static --libs glfw3` -lvulkan -ldl -lpthread -lX11

Binary file not shown.

View File

@ -1,7 +1,6 @@
#include "first_app.hpp" #include "first_app.hpp"
#include "simple_render_system.hpp" #include "simple_render_system.hpp"
#include "rainbow_system.hpp" #include "rainbow_system.hpp"
#include "gravity_physics_system.hpp"
// libs // libs
#define GLM_FORCE_RADIANS #define GLM_FORCE_RADIANS
@ -25,72 +24,6 @@ namespace hk
{ {
} }
void FirstApp::runGravitySystem()
{
// Gravity Physics System
// Create some Models
std::shared_ptr<Model> squareModel = createSquareModel(m_device, {.5f, .0f});
std::shared_ptr<Model> circleModel = createCircleModel(m_device, 64);
// Create Physics Objects
std::vector<GameObject> physicsObjects{};
auto red = GameObject::createGameObject();
red.m_transform2d.scale = glm::vec2{.05f};
red.m_transform2d.translation = {.5f, .5f};
red.m_color = {1.f, 0.f, 0.f};
red.m_rigidBody2d.velocity = {-.5f, .0f};
red.m_model = circleModel;
physicsObjects.push_back(std::move(red));
auto blue = GameObject::createGameObject();
blue.m_transform2d.scale = glm::vec2{.05f};
blue.m_transform2d.translation = {-.45f, -.25f};
blue.m_color = {0.f, 0.f, 1.f};
blue.m_rigidBody2d.velocity = {.5f, .0f};
blue.m_model = circleModel;
physicsObjects.push_back(std::move(blue));
// Create Vector Field
std::vector<GameObject> vectorField{};
int gridCount = 40;
for (int i = 0; i < gridCount; i++)
{
for(int j = 0; j < gridCount; j ++)
{
auto vf = GameObject::createGameObject();
vf.m_transform2d.scale = glm::vec2{0.005f};
vf.m_transform2d.translation = {-1.0f + (i + 0.5f) * 2.0f / gridCount, -1.0f + (j + 0.5f) * 2.0f / gridCount};
vf.m_color = glm::vec3{1.0f};
vf.m_model = squareModel;
vectorField.push_back(std::move(vf));
}
}
GravityPhysicsSystem gravitySystem(0.81f);
Vec2FieldSystem vecFieldSystem{};
SimpleRenderSystem simpleRenderSystem{m_device, m_renderer.getSwapChainRenderPass()};
while(!m_window.shouldClose())
{
glfwPollEvents();
if (auto commandBuffer = m_renderer.beginFrame())
{
// update system
gravitySystem.update(physicsObjects, 1.f / 60, 5);
vecFieldSystem.update(gravitySystem, physicsObjects, vectorField);
// render system
m_renderer.beginSwapChainRenderPass(commandBuffer);
simpleRenderSystem.renderGameObjects(commandBuffer, physicsObjects);
simpleRenderSystem.renderGameObjects(commandBuffer, vectorField);
m_renderer.endSwapChainRenderPass(commandBuffer);
m_renderer.endFrame();
}
vkDeviceWaitIdle(m_device.device());
}
}
void FirstApp::run() void FirstApp::run()
{ {
SimpleRenderSystem simpleRenderSystem{m_device, m_renderer.getSwapChainRenderPass()}; SimpleRenderSystem simpleRenderSystem{m_device, m_renderer.getSwapChainRenderPass()};
@ -115,22 +48,74 @@ namespace hk
vkDeviceWaitIdle(m_device.device()); vkDeviceWaitIdle(m_device.device());
} }
void FirstApp::loadGameObjects() // temporary helper function, creates a 1x1x1 cube centered at offset
std::unique_ptr<Model> createCubeModel(hk::Device &device, glm::vec3 offset)
{ {
std::vector<Model::Vertex> vertices{ std::vector<Model::Vertex> vertices{
{{0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}}, // left face (white)
{{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}} {{-.5f, -.5f, -.5f}, {.9f, .9f, .9f}},
{{-.5f, .5f, .5f}, {.9f, .9f, .9f}},
{{-.5f, -.5f, .5f}, {.9f, .9f, .9f}},
{{-.5f, -.5f, -.5f}, {.9f, .9f, .9f}},
{{-.5f, .5f, -.5f}, {.9f, .9f, .9f}},
{{-.5f, .5f, .5f}, {.9f, .9f, .9f}},
// right face (yellow)
{{.5f, -.5f, -.5f}, {.8f, .8f, .1f}},
{{.5f, .5f, .5f}, {.8f, .8f, .1f}},
{{.5f, -.5f, .5f}, {.8f, .8f, .1f}},
{{.5f, -.5f, -.5f}, {.8f, .8f, .1f}},
{{.5f, .5f, -.5f}, {.8f, .8f, .1f}},
{{.5f, .5f, .5f}, {.8f, .8f, .1f}},
// top face (orange, remember y axis points down)
{{-.5f, -.5f, -.5f}, {.9f, .6f, .1f}},
{{.5f, -.5f, .5f}, {.9f, .6f, .1f}},
{{-.5f, -.5f, .5f}, {.9f, .6f, .1f}},
{{-.5f, -.5f, -.5f}, {.9f, .6f, .1f}},
{{.5f, -.5f, -.5f}, {.9f, .6f, .1f}},
{{.5f, -.5f, .5f}, {.9f, .6f, .1f}},
// bottom face (red)
{{-.5f, .5f, -.5f}, {.8f, .1f, .1f}},
{{.5f, .5f, .5f}, {.8f, .1f, .1f}},
{{-.5f, .5f, .5f}, {.8f, .1f, .1f}},
{{-.5f, .5f, -.5f}, {.8f, .1f, .1f}},
{{.5f, .5f, -.5f}, {.8f, .1f, .1f}},
{{.5f, .5f, .5f}, {.8f, .1f, .1f}},
// nose face (blue)
{{-.5f, -.5f, 0.5f}, {.1f, .1f, .8f}},
{{.5f, .5f, 0.5f}, {.1f, .1f, .8f}},
{{-.5f, .5f, 0.5f}, {.1f, .1f, .8f}},
{{-.5f, -.5f, 0.5f}, {.1f, .1f, .8f}},
{{.5f, -.5f, 0.5f}, {.1f, .1f, .8f}},
{{.5f, .5f, 0.5f}, {.1f, .1f, .8f}},
// tail face (green)
{{-.5f, -.5f, -0.5f}, {.1f, .8f, .1f}},
{{.5f, .5f, -0.5f}, {.1f, .8f, .1f}},
{{-.5f, .5f, -0.5f}, {.1f, .8f, .1f}},
{{-.5f, -.5f, -0.5f}, {.1f, .8f, .1f}},
{{.5f, -.5f, -0.5f}, {.1f, .8f, .1f}},
{{.5f, .5f, -0.5f}, {.1f, .8f, .1f}},
}; };
auto model = std::make_shared<Model>(m_device, vertices); for (auto &v : vertices)
{
v.position += offset;
}
return std::make_unique<Model>(device, vertices);
}
auto triangle = GameObject::createGameObject(); void FirstApp::loadGameObjects()
triangle.m_model = model; {
triangle.m_color = {.1f, .8f, .1f}; std::shared_ptr<Model> cubeModel = createCubeModel(m_device, {0.f, 0.f, 0.f});
triangle.m_transform2d.translation.x = .2f; auto cube = GameObject::createGameObject();
triangle.m_transform2d.scale = {2.0f, .5f}; cube.m_model = cubeModel;
triangle.m_transform2d.rotation = .25f * glm::two_pi<float>(); cube.m_transform.translation = {0.f, 0.f, .5f};
cube.m_transform.scale = {.5f, .5f, .5f};
m_gameObjects.push_back(std::move(triangle)); m_gameObjects.push_back(std::move(cube));
} }
} // namespace hk } // namespace hk

View File

@ -1,129 +0,0 @@
#pragma once
#include <hk_game_object.hpp>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
// std
#include <memory>
#include <random>
#include <vector>
namespace hk
{
class GravityPhysicsSystem
{
public:
GravityPhysicsSystem(float strength) :m_strengthGravity(strength) {}
void update(std::vector<GameObject> &objs, float dt, unsigned int substeps = 1)
{
const float stepDelta = dt / substeps;
for (int i = 0; i < substeps; i++)
{
stepSimulation(objs, stepDelta);
}
}
glm::vec2 computeForce(GameObject &fromObj, GameObject &toObj) const
{
auto offset = fromObj.m_transform2d.translation - toObj.m_transform2d.translation;
float distanceSquared = glm::dot(offset, offset);
if (glm::abs(distanceSquared < 1e-10f))
{
return {.0f, .0f};
}
float force = m_strengthGravity * toObj.m_rigidBody2d.mass * fromObj.m_rigidBody2d.mass / distanceSquared;
return force * offset / glm::sqrt(distanceSquared);
}
const float m_strengthGravity;
private:
void stepSimulation(std::vector<GameObject> &physicsObjs, float dt)
{
// Loops throught all pairs of objects and applies attractive force between them
for (auto iterA = physicsObjs.begin(); iterA != physicsObjs.end(); ++iterA)
{
auto &objA = *iterA;
for (auto iterB = iterA; iterB != physicsObjs.end(); ++iterB)
{
if (iterA == iterB) continue;
auto &objB = *iterB;
auto force = computeForce(objA, objB);
objA.m_rigidBody2d.velocity += dt * -force / objA.m_rigidBody2d.mass;
objB.m_rigidBody2d.velocity += dt * force / objB.m_rigidBody2d.mass;
}
}
// update each objects position based on its final velocity
for (auto &obj : physicsObjs)
{
obj.m_transform2d.translation += dt * obj.m_rigidBody2d.velocity;
}
}
};
class Vec2FieldSystem
{
public:
void update(const GravityPhysicsSystem &physicsSystem, std::vector<GameObject> &physicsObjs, std::vector<GameObject> &vectorField)
{
for (auto &vf : vectorField)
{
glm::vec2 direction{};
for (auto &obj : physicsObjs)
{
direction += physicsSystem.computeForce(obj, vf);
}
vf.m_transform2d.scale.x = 0.005f + 0.045f * glm::clamp(glm::log(glm::length(direction) + 1) / 3.f, 0.f, 1.f);
vf.m_transform2d.rotation = atan2(direction.y, direction.x);
}
}
};
std::unique_ptr<Model> createSquareModel(Device &device, glm::vec2 offset)
{
std::vector<Model::Vertex> vertices = {
{{-0.5f, -0.5f}}, {{0.5f, 0.5f}},
{{-0.5f, 0.5f}}, {{-0.5f, -0.5f}},
{{0.5f, -0.5f}}, {{0.5f, 0.5f}},
};
for (auto &v : vertices)
{
v.posision += offset;
}
return std::make_unique<Model>(device, vertices);
}
std::unique_ptr<Model> createCircleModel(Device &device, unsigned int numSides)
{
std::vector<Model::Vertex> uniqueVertices{};
for(int i = 0; i < numSides; i++)
{
float angle = i * glm::two_pi<float>() / numSides;
uniqueVertices.push_back({{glm::cos(angle), glm::sin(angle)}});
}
uniqueVertices.push_back({});
std::vector<Model::Vertex> vertices{};
for(int i = 0; i < numSides; i++)
{
vertices.push_back(uniqueVertices[i]);
vertices.push_back(uniqueVertices[(i+1) % numSides]);
vertices.push_back(uniqueVertices[numSides]);
}
return std::make_unique<Model>(device, vertices);
}
}

View File

@ -2,29 +2,51 @@
#include "hk_model.hpp" #include "hk_model.hpp"
// libs
#include <glm/gtc/matrix_transform.hpp>
// std // std
#include <memory> #include <memory>
namespace hk namespace hk
{ {
struct Transform2dComponent struct TransformComponent
{ {
glm::vec2 translation{}; glm::vec3 translation{};
glm::vec2 scale{1.0f, 1.0f}; glm::vec3 scale{1.0f, 1.0f, 1.0f};
float rotation; glm::vec3 rotation{};
glm::mat2 mat2()
// Matrix corrsponds to Translate * Ry * Rx * Rz * Scale
// Rotations correspond to Tait-bryan angles of Y(1), X(2), Z(3)
// https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
glm::mat4 mat4()
{ {
const float cos = glm::cos(rotation); const float c3 = glm::cos(rotation.z);
const float sin = glm::sin(rotation); const float s3 = glm::sin(rotation.z);
glm::mat2 rotationMat{ const float c2 = glm::cos(rotation.x);
{cos, sin}, const float s2 = glm::sin(rotation.x);
{-sin, cos}}; const float c1 = glm::cos(rotation.y);
const float s1 = glm::sin(rotation.y);
glm::mat2 scaleMat{ return glm::mat4{
{scale.x, .0f}, {
{.0f, scale.y}}; scale.x * (c1 * c3 + s1 * s2 * s3),
scale.x * (c2 * s3),
return rotationMat * scaleMat; scale.x * (c1 * s2 * s3 - c3 * s1),
0.0f,
},
{
scale.y * (c3 * s1 * s2 - c1 * s3),
scale.y * (c2 * c3),
scale.y * (c1 * c3 * s2 + s1 * s3),
0.0f,
},
{
scale.z * (c2 * s1),
scale.z * (-s2),
scale.z * (c1 * c2),
0.0f,
},
{translation.x, translation.y, translation.z, 1.0f}};
} }
}; };
@ -55,7 +77,7 @@ namespace hk
std::shared_ptr<Model> m_model{}; std::shared_ptr<Model> m_model{};
glm::vec3 m_color{}; glm::vec3 m_color{};
Transform2dComponent m_transform2d{}; TransformComponent m_transform{};
RigidBody2dComponent m_rigidBody2d{}; RigidBody2dComponent m_rigidBody2d{};

View File

@ -58,8 +58,8 @@ namespace hk{
std::vector<VkVertexInputAttributeDescription> attributeDescriptions(2); std::vector<VkVertexInputAttributeDescription> attributeDescriptions(2);
attributeDescriptions[0].binding = 0; attributeDescriptions[0].binding = 0;
attributeDescriptions[0].location = 0; attributeDescriptions[0].location = 0;
attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[0].offset = offsetof(Vertex, posision); attributeDescriptions[0].offset = offsetof(Vertex, position);
attributeDescriptions[1].binding = 0; attributeDescriptions[1].binding = 0;
attributeDescriptions[1].location = 1; attributeDescriptions[1].location = 1;

View File

@ -13,7 +13,7 @@ namespace hk{
{ {
public: public:
struct Vertex { struct Vertex {
glm::vec2 posision; glm::vec3 position;
glm::vec3 color; glm::vec3 color;
static std::vector<VkVertexInputBindingDescription> getBindingDescriptions(); static std::vector<VkVertexInputBindingDescription> getBindingDescriptions();

View File

@ -5,19 +5,13 @@
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#define GRAVITY_SYSTEM 0
int main() int main()
{ {
hk::FirstApp app{}; hk::FirstApp app{};
try try
{ {
#if GRAVITY_SYSTEM == 1
app.runGravitySystem();
#else
app.run(); app.run();
#endif
} }
catch(const std::exception& e) catch(const std::exception& e)
{ {

View File

@ -1,13 +1,13 @@
#version 450 #version 450
layout (location = 0) in vec3 fragColor;
layout (location = 0) out vec4 outColor; layout (location = 0) out vec4 outColor;
layout(push_constant) uniform Push{ layout(push_constant) uniform Push{
mat2 transform; mat4 transform;
vec2 offset;
vec3 color; vec3 color;
} push; } push;
void main(){ void main(){
outColor = vec4(push.color, 1.0); outColor = vec4(fragColor, 1.0);
} }

Binary file not shown.

View File

@ -1,14 +1,16 @@
#version 450 #version 450
layout(location = 0) in vec2 position; layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color; layout(location = 1) in vec3 color;
layout(location = 0) out vec3 fragColor;
layout(push_constant) uniform Push{ layout(push_constant) uniform Push{
mat2 transform; mat4 transform;
vec2 offset;
vec3 color; vec3 color;
} push; } push;
void main(){ void main(){
gl_Position = vec4(push.transform * position + push.offset, 0.0, 1.0); gl_Position = push.transform * vec4(position, 1.0);
fragColor = color;
} }

Binary file not shown.

View File

@ -14,8 +14,7 @@ namespace hk
{ {
struct SimplePushConstantData struct SimplePushConstantData
{ {
glm::mat2 transform{1.0f}; glm::mat4 transform{1.0f};
glm::vec2 offset;
alignas(16) glm::vec3 color; alignas(16) glm::vec3 color;
}; };
@ -72,12 +71,12 @@ namespace hk
m_pipeline->bind(commandBuffer); m_pipeline->bind(commandBuffer);
for (auto &obj : gameObjects) for (auto &obj : gameObjects)
{ {
obj.m_transform2d.rotation = glm::mod(obj.m_transform2d.rotation + 0.01f, glm::two_pi<float>()); obj.m_transform.rotation.y = glm::mod(obj.m_transform.rotation.y + 0.01f, glm::two_pi<float>());
obj.m_transform.rotation.x = glm::mod(obj.m_transform.rotation.x + 0.005f, glm::two_pi<float>());
SimplePushConstantData push{}; SimplePushConstantData push{};
push.offset = obj.m_transform2d.translation;
push.color = obj.m_color; push.color = obj.m_color;
push.transform = obj.m_transform2d.mat2(); push.transform = obj.m_transform.mat4();
vkCmdPushConstants( vkCmdPushConstants(
commandBuffer, commandBuffer,