init commit

This commit is contained in:
2025-07-31 10:53:33 +08:00
commit 28816a76b3
30 changed files with 10237 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.idea/
cmake-build-debug/
cmake-build-release/

14
CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.31)
set(PROJECT_NAME Rasterizer)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
project(${PROJECT_NAME})
set(CMAKE_CXX_STANDARD 20)
file(GLOB_RECURSE SRC_SOURCE
src/**.cpp
src/vendor/stb/stb_image.cpp
)
add_executable(${PROJECT_NAME} ${SRC_SOURCE})

8
README.md Normal file
View File

@ -0,0 +1,8 @@
## build by cmake
```bash
cmake -B build
cmake --build build --config Release -j18
```

View File

@ -0,0 +1,274 @@
//
// Created by sfd on 25-7-28.
//
#include "Application.h"
#include <fstream>
#include <iostream>
#include <map>
#include "../Core/Renderer/Renderer.h"
#include "../Shader/DemoShader.h"
Application::Application(std::string name, const int width, const int height, CommandLineArgs args)
: m_Name(std::move(name)),m_Width(width), m_Height(height), m_Window(nullptr)
{
if (args.argc != 2)
{
std::string programName = args.argv[0];
std::cout << "should in command to use it\n" << std::endl;
std::cout << " Usage: " << programName.substr(programName.find_last_of("\\/") + 1) << " path/to/model.obj\n" << std::endl;
std::cout << " Or drag \"obj\" file in this program\n" << std::endl;
system("pause");
exit(0);
}
Init();
const std::string path = args.argv[1];
const std::string pathDir = path.substr(0, path.find_last_of("/\\"));
std::string mtllib;
LoadMesh(path.c_str(), mtllib);
if (!mtllib.empty())
{
std::map<std::string, std::string> textures;
if (GetTexture((pathDir + "\\" + mtllib).c_str(), textures))
{
m_Uniform.Diffuse = Texture::CreateTexture(pathDir + "\\" + textures["diff"]);
m_Uniform.Specular = Texture::CreateTexture(pathDir + "\\" + textures["spec"]);
}
}
}
void Application::Init()
{
m_Window = Window::Create(m_Name, m_Width, m_Height);
m_LastFrameTime = std::chrono::steady_clock::now();
}
void Application::Run()
{
while (!m_Window->Close())
{
auto nowFrameTime = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(nowFrameTime - m_LastFrameTime);
const float deltaTime = duration.count() * 0.001f * 0.001f;
m_LastFrameTime = nowFrameTime;
OnUpdate(deltaTime);
Window::PollInputEvents();
}
}
void Application::Terminate()
{
delete m_Uniform.Diffuse;
delete m_Uniform.Specular;
m_Window->Terminate();
delete m_Window;
}
Application::~Application()
{
Terminate();
}
void Application::OnUpdate(float ts)
{
static float fpstime = 0.0f;
static int fpsCount = 0;
fpstime +=ts;
fpsCount += 1;
if (fpstime >= 0.5f)
{
std::cout << "fps: "<< (int)((float)fpsCount/fpstime) << std::endl;
fpstime = 0.0f;
fpsCount = 0;
}
OnCameraUpdate(ts);
FrameBuffer framebuffer(m_Width, m_Height);
framebuffer.Clear({0.2f, 0.2f, 0.2f});
Math::Mat4 view = Math::Mat4LookAt(m_Camera.Position, m_Camera.Position + m_Camera.Front, {0.0f, 1.0f, 0.0f});
Math::Mat4 projection = Math::Mat4Perspective(90.f/180.f *Math::PI, m_Camera.Aspect, 0.1f ,100.0f);
Math::Mat4 model = Math::Mat4Identity();
m_Uniform.MVP = projection * view * model;
m_Uniform.Camera = m_Camera.Position;
m_Uniform.Model = model;
m_Uniform.ModelNormalToWorld = Math::Mat4Identity();
Program program(DemoVertexShader, DemoFragmentShader);
for (const auto& triangle : m_Meshes)
{
Renderer::Draw(framebuffer, program, triangle, m_Uniform);
}
m_Window->DrawFrameBuffer(framebuffer);
}
void Application::OnCameraUpdate(float ts)
{
constexpr float speed = 1.0f;
if (m_Window->GetKey(KEY_SPACE) == KEY_PRESS)
m_Camera.Position += speed * ts * m_Camera.Up;
if (m_Window->GetKey(KEY_LEFT_CONTROL) == KEY_PRESS)
m_Camera.Position -= speed * ts * m_Camera.Up;
if (m_Window->GetKey(KEY_W) == KEY_PRESS)
m_Camera.Position += speed * ts * m_Camera.Front;
if (m_Window->GetKey(KEY_S) == KEY_PRESS)
m_Camera.Position -= speed * ts * m_Camera.Front;
if (m_Window->GetKey(KEY_A) == KEY_PRESS)
m_Camera.Position -= speed * ts * m_Camera.Right;
if (m_Window->GetKey(KEY_D) == KEY_PRESS)
m_Camera.Position += speed * ts * m_Camera.Right;
// std::cout << m_Camera.Position << std::endl;
constexpr float rotationSpeed = 1.0f;
Math::Mat4 rotation = Math::Mat4Identity();
if (m_Window->GetKey(KEY_Q) == KEY_PRESS)
rotation = Math::Mat4RotateY(ts * rotationSpeed);
if (m_Window->GetKey(KEY_E) == KEY_PRESS)
rotation = Math::Mat4RotateY(-ts * rotationSpeed);
m_Camera.Front = rotation * m_Camera.Front;
m_Camera.Front = {Math::Normalize(m_Camera.Front), 0.0f};
m_Camera.Right = {Math::Normalize(Math::Cross(m_Camera.Front, Math::Vec3(0,1,0))), 0.0f };
}
void Application::LoadMesh(const std::string& filepath, std::string& mtllib)
{
if (filepath.substr(filepath.find_last_of('.')) != ".obj")
{
std::cout << "not a .obj file \n\t" << filepath << std::endl;
Terminate();
system("pause");
exit(0);
}
std::ifstream file(filepath);
if (!file)
{
std::cout << "Failed to load file! is exist? \n\t" << filepath << std::endl;
Terminate();
system("pause");
exit(0);
}
std::vector<Math::Vec3> positions;
std::vector<Math::Vec2> texCoords;
std::vector<Math::Vec3> normals;
std::vector<unsigned int> posIndices;
std::vector<unsigned int> texIndices;
std::vector<unsigned int> normalIndices;
std::string line;
while (!file.eof())
{
std::getline(file, line);
int items = -1;
if (line.find("mtllib ") == 0)
{
mtllib = line.substr(7);
}
else if (line.find("v ") == 0)
{
Math::Vec3 position;
items = sscanf(line.c_str(), "v %f %f %f", &position.x, &position.y, &position.z);
ASSERT(items == 3);
positions.push_back(position);
}else if (line.find("vt ") == 0)
{
Math::Vec2 texCoord;
items = sscanf(line.c_str(), "vt %f %f", &texCoord.x, &texCoord.y);
ASSERT(items == 2);
texCoords.push_back(texCoord);
}else if (line.find("vn ") == 0)
{
Math::Vec3 normal;
items = sscanf(line.c_str(), "vn %f %f %f", &normal.x, &normal.y, &normal.z);
ASSERT(items == 3);
normals.push_back(normal);
}else if (line.find("f ") == 0)
{
int pIndices[3], uvIndices[3], nIndices[3];
items = sscanf(line.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d",
&pIndices[0], &uvIndices[0], &nIndices[0],
&pIndices[1], &uvIndices[1], &nIndices[1],
&pIndices[2], &uvIndices[2], &nIndices[2]);
ASSERT(items == 9);
for (int i = 0; i < 3; i++)
{
posIndices.push_back(pIndices[i] - 1);
texIndices.push_back(uvIndices[i] - 1);
normalIndices.push_back(nIndices[i] - 1);
}
}
}
file.close();
int triNum = posIndices.size() / 3;
for (int i = 0; i < triNum; i++)
{
Triangle<BlinnVertex> triangle;
for (int j = 0; j < 3; j++)
{
int index = 3 * i + j;
int posIndex = posIndices[index];
int texIndex = texIndices[index];
int normIndex = normalIndices[index];
triangle[j].ModelPosition = {positions[posIndex], 1.0f };
triangle[j].TexCoord = texCoords[texIndex];
triangle[j].ModelNormal = normals[normIndex];
}
m_Meshes.emplace_back(triangle);
}
}
bool Application::GetTexture(const char* filepath, std::map<std::string, std::string>& textures)
{
std::ifstream file(filepath);
// ASSERT(file);
if (!file)
{
std::cout << "Failed to open file: " << filepath << std::endl;
return false;
}
bool hasTexture = false;
std::string line;
while (!file.eof())
{
std::getline(file, line);
if (line.find("map_Kd ") == 0)
{
textures["diff"] = line.substr(7);
hasTexture = true;
}else if (line.find("map_Ks ") == 0)
{
textures["spec"] = line.substr(7);
hasTexture = true;
}
}
return hasTexture;
}

View File

@ -0,0 +1,57 @@
//
// Created by sfd on 25-7-28.
//
#ifndef APPLICATION_H
#define APPLICATION_H
#include <chrono>
#include <map>
#include "../Core/Camera/Camera.h"
#include "../Core/Renderer/Renderer.h"
#include "../Core/Window/Window.h"
#include "../Shader/DemoShader.h"
struct CommandLineArgs
{
int argc;
char **argv;
};
class Application {
public:
Application(std::string name, int width, int height, CommandLineArgs args = CommandLineArgs());
~Application();
void Run();
private:
void Init();
void Terminate();
void OnUpdate(float ts);
void OnCameraUpdate(float ts);
void LoadMesh(const std::string& filepath ,std::string& mtllib);
bool GetTexture(const char* filepath, std::map<std::string, std::string>& textures);
private:
std::string m_Name;
int m_Width, m_Height;
Window* m_Window;
std::vector<Triangle<BlinnVertex>> m_Meshes;
BlinnUniform m_Uniform;
std::chrono::steady_clock::time_point m_LastFrameTime;
Camera m_Camera;
};
#endif //APPLICATION_H

5
src/Core/Assert.cpp Normal file
View File

@ -0,0 +1,5 @@
//
// Created by sfd on 25-7-28.
//
#include "Assert.h"

12
src/Core/Assert.h Normal file
View File

@ -0,0 +1,12 @@
//
// Created by sfd on 25-7-28.
//
#ifndef ASSERT_H
#define ASSERT_H
#define LOG(...)
#define ASSERT(x, ...) {if(!(x)) {LOG(__VA_ARGS__); __debugbreak(); }}
#endif //ASSERT_H

View File

@ -0,0 +1,5 @@
//
// Created by sfd on 25-7-30.
//
#include "Camera.h"

21
src/Core/Camera/Camera.h Normal file
View File

@ -0,0 +1,21 @@
//
// Created by sfd on 25-7-30.
//
#ifndef CAMERA_H
#define CAMERA_H
#include "../Math.h"
class Camera
{
public:
Math::Vec4 Position{0.0f, 0.0f, 3.0f, 0.0f};
Math::Vec4 Right{1.0f, 0.0f, 0.0f, 0.0f};
Math::Vec4 Up{0.0f, 1.0f, 0.0f, 0.0f};
Math::Vec4 Front{0.0f, 0.0f, -1.0f, 0.0f};
float Aspect = 1.0f;
};
#endif //CAMERA_H

View File

@ -0,0 +1,5 @@
//
// Created by sfd on 25-7-28.
//
#include "InputCodes.h"

144
src/Core/Input/InputCodes.h Normal file
View File

@ -0,0 +1,144 @@
//
// Created by sfd on 25-7-28.
//
#ifndef INPUTCODES_H
#define INPUTCODES_H
// From glfw3.h
#define KEY_SPACE 32
#define KEY_APOSTROPHE 39 /* ' */
#define KEY_COMMA 44 /* , */
#define KEY_MINUS 45 /* - */
#define KEY_PERIOD 46 /* . */
#define KEY_SLASH 47 /* / */
#define KEY_0 48
#define KEY_1 49
#define KEY_2 50
#define KEY_3 51
#define KEY_4 52
#define KEY_5 53
#define KEY_6 54
#define KEY_7 55
#define KEY_8 56
#define KEY_9 57
#define KEY_SEMICOLON 59 /* ; */
#define KEY_EQUAL 61 /* = */
#define KEY_A 65
#define KEY_B 66
#define KEY_C 67
#define KEY_D 68
#define KEY_E 69
#define KEY_F 70
#define KEY_G 71
#define KEY_H 72
#define KEY_I 73
#define KEY_J 74
#define KEY_K 75
#define KEY_L 76
#define KEY_M 77
#define KEY_N 78
#define KEY_O 79
#define KEY_P 80
#define KEY_Q 81
#define KEY_R 82
#define KEY_S 83
#define KEY_T 84
#define KEY_U 85
#define KEY_V 86
#define KEY_W 87
#define KEY_X 88
#define KEY_Y 89
#define KEY_Z 90
#define KEY_LEFT_BRACKET 91 /* [ */
#define KEY_BACKSLASH 92 /* \ */
#define KEY_RIGHT_BRACKET 93 /* ] */
#define KEY_GRAVE_ACCENT 96 /* ` */
#define KEY_WORLD_1 161 /* non-US #1 */
#define KEY_WORLD_2 162 /* non-US #2 */
/* Functkeys */
#define KEY_ESCAPE 256
#define KEY_ENTER 257
#define KEY_TAB 258
#define KEY_BACKSPACE 259
#define KEY_INSERT 260
#define KEY_DELETE 261
#define KEY_RIGHT 262
#define KEY_LEFT 263
#define KEY_DOWN 264
#define KEY_UP 265
#define KEY_PAGE_UP 266
#define KEY_PAGE_DOWN 267
#define KEY_HOME 268
#define KEY_END 269
#define KEY_CAPS_LOCK 280
#define KEY_SCROLL_LOCK 281
#define KEY_NUM_LOCK 282
#define KEY_PRINT_SCREEN 283
#define KEY_PAUSE 284
#define KEY_F1 290
#define KEY_F2 291
#define KEY_F3 292
#define KEY_F4 293
#define KEY_F5 294
#define KEY_F6 295
#define KEY_F7 296
#define KEY_F8 297
#define KEY_F9 298
#define KEY_F10 299
#define KEY_F11 300
#define KEY_F12 301
#define KEY_F13 302
#define KEY_F14 303
#define KEY_F15 304
#define KEY_F16 305
#define KEY_F17 306
#define KEY_F18 307
#define KEY_F19 308
#define KEY_F20 309
#define KEY_F21 310
#define KEY_F22 311
#define KEY_F23 312
#define KEY_F24 313
#define KEY_F25 314
#define KEY_KP_0 320
#define KEY_KP_1 321
#define KEY_KP_2 322
#define KEY_KP_3 323
#define KEY_KP_4 324
#define KEY_KP_5 325
#define KEY_KP_6 326
#define KEY_KP_7 327
#define KEY_KP_8 328
#define KEY_KP_9 329
#define KEY_KP_DECIMAL 330
#define KEY_KP_DIVIDE 331
#define KEY_KP_MULTIPLY 332
#define KEY_KP_SUBTRACT 333
#define KEY_KP_ADD 334
#define KEY_KP_ENTER 335
#define KEY_KP_EQUAL 336
#define KEY_LEFT_SHIFT 340
#define KEY_LEFT_CONTROL 341
#define KEY_LEFT_ALT 342
#define KEY_LEFT_SUPER 343
#define KEY_RIGHT_SHIFT 344
#define KEY_RIGHT_CONTROL 345
#define KEY_RIGHT_ALT 346
#define KEY_RIGHT_SUPER 347
#define KEY_MENU 348
#define KEY_MAX_COUNT 512
#define BUTTON_LEFT 0
#define BUTTON_RIGHT 1
#define BUTTON_MAX_COUNT 8
#define KEY_RELEASE 0
#define KEY_PRESS 1
#endif //INPUTCODES_H

322
src/Core/Math.cpp Normal file
View File

@ -0,0 +1,322 @@
//
// Created by sfd on 25-7-28.
//
#include "Math.h"
#include <cmath>
namespace Math
{
Vec2 operator+ (const Vec2& left, const Vec2& right)
{
return Vec2{ left.x + right.x, left.y + right.y };
}
Vec2 operator- (const Vec2& left, const Vec2& right)
{
return Vec2{ left.x - right.x, left.y - right.y };
}
Vec3 operator+ (const Vec3& left, const Vec3& right)
{
return Vec3{ left.x + right.x, left.y + right.y, left.z + right.z };
}
Vec3 operator- (const Vec3& left, const Vec3& right)
{
return left + (-1.0f * right);
}
Vec3 operator* (const float left, const Vec3& right)
{
return Vec3{ left * right.x, left * right.y, left * right.z };
}
Vec3 operator* (const Vec3& left, const float right)
{
return right * left;
}
Vec3 operator* (const Vec3& left, const Vec3& right)
{
return { left.x * right.x, left.y * right.y , left.z * right.z };
}
Vec3 operator/ (const Vec3& left, const float right)
{
ASSERT(right != 0);
return left * (1.0f / right);
}
float Dot(const Vec3& left, const Vec3& right)
{
return left.x * right.x + left.y * right.y + left.z * right.z;
}
Vec3 Cross(const Vec3& left, const Vec3& right)
{
float x = left.y * right.z - left.z * right.y;
float y = left.z * right.x - left.x * right.z;
float z = left.x * right.y - left.y * right.x;
return { x, y, z };
}
Vec3 Normalize(const Vec3& v)
{
float len = (float)std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
ASSERT(len != 0);
return v / len;
}
Vec4 operator+ (const Vec4& left, const Vec4& right)
{
return Vec4{ left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w };
}
Vec4 operator- (const Vec4& left, const Vec4& right)
{
return Vec4{ left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w };
}
Vec4 operator+=(Vec4& left, const Vec4& right)
{
left = left + right;
return left;
}
Vec4 operator-=(Vec4& left, const Vec4& right)
{
left = left - right;
return left;
}
Vec4 operator* (const float left, const Vec4& right)
{
return Vec4{ left * right.x, left * right.y, left * right.z, left * right.w };
}
Vec4 operator* (const Vec4& left, const float right)
{
return right * left;
}
Vec4 operator/ (const Vec4& left, const float right)
{
ASSERT(right != 0);
return left * (1.0f / right);
}
Vec4 operator* (const Mat4& mat4, const Vec4& vec4)
{
Vec4 res;
res.x = mat4.m[0][0] * vec4.x + mat4.m[0][1] * vec4.y + mat4.m[0][2] * vec4.z + mat4.m[0][3] * vec4.w;
res.y = mat4.m[1][0] * vec4.x + mat4.m[1][1] * vec4.y + mat4.m[1][2] * vec4.z + mat4.m[1][3] * vec4.w;
res.z = mat4.m[2][0] * vec4.x + mat4.m[2][1] * vec4.y + mat4.m[2][2] * vec4.z + mat4.m[2][3] * vec4.w;
res.w = mat4.m[3][0] * vec4.x + mat4.m[3][1] * vec4.y + mat4.m[3][2] * vec4.z + mat4.m[3][3] * vec4.w;
return res;
}
Mat4::Mat4()
{
for (int i =0 ; i < 4; i++)
for (int j =0 ; j < 4; j++)
m[i][j] = 0.0f;
}
Mat4::Mat4(const Vec4& v0, const Vec4& v1, const Vec4& v2, const Vec4& v3)
{
m[0][0] = v0.x; m[1][0] = v0.y; m[2][0] = v0.z; m[3][0] = v0.w;
m[0][1] = v1.x; m[1][1] = v1.y; m[2][1] = v1.z; m[3][1] = v1.w;
m[0][2] = v2.x; m[1][2] = v2.y; m[2][2] = v2.z; m[3][2] = v2.w;
m[0][3] = v3.x; m[1][3] = v3.y; m[2][3] = v3.z; m[3][3] = v3.w;
}
Mat4::operator std::string() const
{
std::string res;
res += "[";
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
res += std::to_string(m[i][j]);
res += (i == 3 && j == 3) ? "]" : ", ";
}
res += "\n";
}
return res;
}
Mat4 operator* (const Mat4& left, const Mat4& right)
{
Mat4 res;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 4; k++)
{
res.m[i][j] += left.m[i][k] * right.m[k][j];
}
}
}
return res;
}
Mat4& operator*= (Mat4& left, const Mat4& right)
{
left = left * right;
return left;
}
Mat4 Mat4Identity()
{
return Mat4({ 1,0,0,0 }, { 0,1,0,0 }, { 0,0,1,0 }, { 0,0,0,1 });
}
Mat4 Mat4Translate(float tx, float ty, float tz)
{
Mat4 m = Mat4Identity();
m.m[0][3] = tx;
m.m[1][3] = ty;
m.m[2][3] = tz;
return m;
}
Mat4 Mat4Scale(float sx, float sy, float sz)
{
Mat4 m = Mat4Identity();
ASSERT(sx != 0 && sy != 0 && sz != 0);
m.m[0][0] = sx;
m.m[1][1] = sy;
m.m[2][2] = sz;
return m;
}
Mat4 Mat4RotateX(float angle)
{
float c = (float)std::cos(angle);
float s = (float)std::sin(angle);
Mat4 m = Mat4Identity();
m.m[1][1] = c;
m.m[1][2] = -s;
m.m[2][1] = s;
m.m[2][2] = c;
return m;
}
Mat4 Mat4RotateY(float angle)
{
float c = (float)std::cos(angle);
float s = (float)std::sin(angle);
Mat4 m = Mat4Identity();
m.m[2][2] = c;
m.m[2][0] = -s;
m.m[0][2] = s;
m.m[0][0] = c;
return m;
}
Mat4 Mat4RotateZ(float angle)
{
float c = (float)std::cos(angle);
float s = (float)std::sin(angle);
Mat4 m = Mat4Identity();
m.m[0][0] = c;
m.m[0][1] = -s;
m.m[1][0] = s;
m.m[1][1] = c;
return m;
}
Mat4 Mat4LookAt(const Vec3& xAxis, const Vec3& yAxis, const Vec3& zAxis, const Vec3& eye)
{
Mat4 m = Mat4Identity();
m.m[0][0] = xAxis.x;
m.m[0][1] = xAxis.y;
m.m[0][2] = xAxis.z;
m.m[1][0] = yAxis.x;
m.m[1][1] = yAxis.y;
m.m[1][2] = yAxis.z;
m.m[2][0] = zAxis.x;
m.m[2][1] = zAxis.y;
m.m[2][2] = zAxis.z;
m.m[0][3] = -Dot(xAxis, eye);
m.m[1][3] = -Dot(yAxis, eye);
m.m[2][3] = -Dot(zAxis, eye);
return m;
}
Mat4 Mat4LookAt(const Vec3& eye, const Vec3& target, const Vec3& up)
{
Vec3 zAxis = Normalize(eye - target);
Vec3 xAxis = Normalize(Cross(up, zAxis));
Vec3 yAxis = Normalize(Cross(zAxis, xAxis));
return Mat4LookAt(xAxis, yAxis, zAxis, eye);
}
/*
* fovy: the field of view angle in the y direction, in radians
* aspect: the aspect ratio, defined as width divided by height
* near, far: the distances to the near and far depth clipping planes
*
* 1/(aspect*tan(fovy/2)) 0 0 0
* 0 1/tan(fovy/2) 0 0
* 0 0 -(f+n)/(f-n) -2fn/(f-n)
* 0 0 -1 0
*
* this is the same as
* float half_h = near * (float)tan(fovy / 2);
* float half_w = half_h * aspect;
* mat4_frustum(-half_w, half_w, -half_h, half_h, near, far);
*
* see http://www.songho.ca/opengl/gl_projectionmatrix.html
*/
Mat4 Mat4Perspective(float fovy, float aspect, float near, float far)
{
float z_range = far - near;
Mat4 m = Mat4Identity();
ASSERT(fovy > 0 && aspect > 0);
ASSERT(near > 0 && far > 0 && z_range > 0);
m.m[1][1] = 1 / (float)std::tan(fovy / 2);
m.m[0][0] = m.m[1][1] / aspect;
m.m[2][2] = -(near + far) / z_range;
m.m[2][3] = -2 * near * far / z_range;
m.m[3][2] = -1;
m.m[3][3] = 0;
return m;
}
float Lerp(const float start, const float end, const float t)
{
return end * t + start * (1.0f - t);
}
Vec3 Lerp(const Vec3& start, const Vec3& end, const float t)
{
return end * t + start * (1.0f - t);
}
Vec4 Lerp(const Vec4& start, const Vec4& end, const float t)
{
return end * t + start * (1.0f - t);
}
unsigned char Float2UChar(const float f)
{
return (unsigned char)(f * 255.0f);
}
float UChar2Float(const unsigned char c)
{
return (float)c / 255.0f;
}
float Clamp(const float val, const float min, const float max)
{
if (val < min)
{
return min;
}
else if (val > max)
{
return max;
}
else
{
return val;
}
}
}

160
src/Core/Math.h Normal file
View File

@ -0,0 +1,160 @@
//
// Created by sfd on 25-7-28.
//
#ifndef MATH_H
#define MATH_H
#include <string>
#include "Assert.h"
namespace Math
{
constexpr float PI = 3.14159265359f;
constexpr float EPSILON = 1e-5f;
struct Vec2
{
float x;
float y;
constexpr Vec2() : x(0.0f), y(0.0f) {}
constexpr Vec2(const float x, const float y) : x(x), y(y) {}
explicit operator std::string() const
{
return "(" + std::to_string(x) + ", " + std::to_string(y) + ")";
}
};
struct Vec3
{
union { float x, r; };
union { float y, g; };
union { float z, b; };
constexpr Vec3() : x(0.0f), y(0.0f), z(0.0f) {}
constexpr Vec3(const Vec2& vec, const float z) : x(vec.x), y(vec.y), z(z) {}
constexpr Vec3(const Vec3& vec) : x(vec.x), y(vec.y), z(vec.z) {}
constexpr Vec3(const float x, const float y, const float z) : x(x), y(y), z(z) {}
explicit operator Vec2() const { return {x, y}; }
explicit operator std::string() const
{
return "(" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(x) + ")";
}
};
struct Vec4
{
union { float x, r; };
union { float y, g; };
union { float z, b; };
union { float w, a; };
constexpr Vec4() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) {}
constexpr Vec4(const float val) : x(val), y(val), z(val), w(val) {}
constexpr Vec4(const Vec2& vec2, const float z, const float w) : x(vec2.x), y(vec2.y), z(z), w(w) {}
constexpr Vec4(const Vec3& vec3, const float w) : x(vec3.x), y(vec3.y), z(vec3.z), w(w) {}
constexpr Vec4(const float x, const float y, const float z, const float w) : x(x), y(y), z(z), w(w) {}
operator Vec2() const { return {x, y}; }
operator Vec3() const { return {x, y, z}; }
explicit operator std::string() const
{
std::string res;
res += '(';
res += std::to_string(x);
res += ',';
res += std::to_string(y);
res += ',';
res += std::to_string(z);
res += ',';
res += std::to_string(w);
res += ')';
res += '\n';
return res;
}
};
struct Mat4
{
float m[4][4];
Mat4();
Mat4(const Vec4& v0, const Vec4& v1, const Vec4& v2, const Vec4& v3);
explicit operator std::string() const;
};
Vec2 operator+(const Vec2& left, const Vec2& right);
Vec2 operator-(const Vec2& left, const Vec2& right);
Vec2 operator*(const Vec2& left, float right);
Vec2 operator+=(Vec2& left, const Vec2& right);
Vec2 operator-=(Vec2& left, const Vec2& right);
Vec3 operator+ (const Vec3& left, const Vec3& right);
Vec3 operator- (const Vec3& left, const Vec3& right);
Vec3 operator* (const Vec3& left, const Vec3& right);
Vec3 operator* (const Vec3& vec, float scalar);
Vec3 operator* (float scalar, const Vec3& vec);
Vec3 operator/ (const Vec3& left, float scalar);
Vec3 operator+= (Vec3& left, const Vec3& right);
Vec3 operator-= (Vec3& left, const Vec3& right);
Vec4 operator+ (const Vec4& left, const Vec4& right);
Vec4 operator- (const Vec4& left, const Vec4& right);
Vec4 operator* (const Mat4& mat4, const Vec4& vec4);
Vec4 operator* (float value, const Vec4& vec4);
Vec4 operator* (const Vec4& vec4, float value);
Vec4 operator/ (const Vec4& vec4, float value);
Vec4 operator+= (Vec4& left, const Vec4& right);
Vec4 operator-= (Vec4& left, const Vec4& right);
Mat4 operator* (const Mat4& left, const Mat4& right);
Mat4& operator*= (Mat4& left, const Mat4& right);
Vec3 Normalize(const Vec3& v);
Mat4 Mat4Identity();
Mat4 Mat4Translate(float tx, float ty, float tz);
Mat4 Mat4Scale(float sx, float sy, float sz);
Mat4 Mat4RotateX(float angle);
Mat4 Mat4RotateY(float angle);
Mat4 Mat4RotateZ(float angle);
Vec3 Cross(const Vec3& left, const Vec3& right);
Vec3 Lerp(const Vec3& start, const Vec3& end, float t);
Mat4 Mat4LookAt(const Vec3& eye, const Vec3& target, const Vec3& up);
float Dot(const Vec3& left, const Vec3& right);
float Lerp(float start, float end, float t);
float Clamp(float val, float min, float max);
/*
*
* 1/(aspect*tan(fovy/2)) 0 0 0
* 0 1/tan(fovy/2) 0 0
* 0 0 -(f+n)(f-n) -2fn/(f-n)
* 0 0 -1 0
*
*/
Mat4 Mat4Perspective(float fovy, float aspect, float zNear, float zFar);
inline unsigned char FloatToUchar(const float f) { return (unsigned char)(f * 255.0f); }
inline float UCharToFloat(const unsigned char c) { return (float)c / 255.0f; }
}
#endif //MATH_H

View File

@ -0,0 +1,89 @@
//
// Created by sfd on 25-7-28.
//
#include "FrameBuffer.h"
#include "../Assert.h"
#include "../../Platform/WindowsWindow.h"
FrameBuffer::FrameBuffer(const int width, const int height)
: m_Width(width), m_Height(height)
{
ASSERT(width > 0 && height > 0);
m_PixelSize = width * height;
m_ColorBuffer = new Math::Vec3[m_PixelSize]();
m_DepthBuffer = new float[m_PixelSize]();
Clear({0.0f, 0.0f, 0.0f});
ClearDepth(1.0f);
}
FrameBuffer::~FrameBuffer()
{
delete[] m_ColorBuffer;
delete[] m_DepthBuffer;
m_ColorBuffer = nullptr;
m_DepthBuffer = nullptr;
}
Math::Vec3 FrameBuffer::GetColor(const int x, const int y) const
{
int index = GetPixelIndex(x, y);
if (index < m_PixelSize && index >= 0)
return m_ColorBuffer[index];
else
{
ASSERT(false);
return Math::Vec3{0.0f, 0.0f, 0.0f};
}
}
void FrameBuffer::SetColor(const int x, const int y, const Math::Vec3& color) const
{
if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height))
{
// ASSERT(false);
return;
}else
{
const int index = GetPixelIndex(x, y);
m_ColorBuffer[index] = color;
}
}
void FrameBuffer::SetDepth(const int x, const int y, const float depth)
{
const int index = GetPixelIndex(x, y);
if (index < m_PixelSize && index >= 0)
m_DepthBuffer[index] = depth;
else
ASSERT(false);
}
float FrameBuffer::GetDepth(const int x, const int y) const
{
int index = GetPixelIndex(x, y);
if (index < m_PixelSize && index >= 0)
return m_DepthBuffer[index];
else
{
ASSERT(false);
return 0.0f;
}
}
void FrameBuffer::Clear(const Math::Vec3& color)
{
for (int i = 0; i < m_PixelSize; i++)
m_ColorBuffer[i] = color;
}
void FrameBuffer::ClearDepth(const float depth)
{
for (int i = 0; i < m_PixelSize; i++)
m_DepthBuffer[i] = depth;
}

View File

@ -0,0 +1,41 @@
//
// Created by sfd on 25-7-28.
//
#ifndef FRAMEBUFFER_H
#define FRAMEBUFFER_H
#include "../Math.h"
class FrameBuffer {
public:
FrameBuffer(int width, int height);
~FrameBuffer();
int GetWidth() const { return m_Width; }
int GetHeight() const { return m_Height; }
Math::Vec3 GetColor(int x, int y) const;
void SetColor(int x, int y, const Math::Vec3& color) const;
void SetDepth(int x, int y, float depth);
float GetDepth(int x, int y) const;
void Clear(const Math::Vec3& color = Math::Vec3{0.0f, 0.0f, 0.0f});
void ClearDepth(float depth = 1.0f);
private:
inline int GetPixelIndex(const int x, const int y) const { return y * m_Width + x; }
private:
int m_Width = 800;
int m_Height = 600;
int m_PixelSize = 0;
float* m_DepthBuffer;
Math::Vec3* m_ColorBuffer;
};
#endif //FRAMEBUFFER_H

View File

@ -0,0 +1,124 @@
//
// Created by sfd on 25-7-28.
//
#include "Renderer.h"
#include <algorithm>
void Renderer::CalculateWeights(float(& screenWeights)[3], float(& weights)[3], const Math::Vec4(& fragCoords)[3], const Math::Vec2& screenPoint)
{
const Math::Vec2 ab = fragCoords[1] - fragCoords[0];
const Math::Vec2 ac = fragCoords[2] - fragCoords[0];
const Math::Vec2 ap = screenPoint - fragCoords[0];
const float factor = 1.0f/(ab.x * ac.y - ab.y * ac.x);
const float s = (ac.y * ap.x - ac.x * ap.y) * factor;
const float t = (ab.x * ap.y - ab.y * ap.x) * factor;
screenWeights[0] = 1-s-t;
screenWeights[1] = s;
screenWeights[2] = t;
const float w0 = fragCoords[0].w * screenWeights[0];
const float w1 = fragCoords[1].w * screenWeights[1];
const float w2 = fragCoords[2].w * screenWeights[2];
const float normalizer = 1.0f/(w0 + w1 + w2);
weights[0] = w0 * normalizer;
weights[1] = w1 * normalizer;
weights[2] = w2 * normalizer;
}
bool Renderer::IsInsideTriangle(float(& weights)[3])
{
return weights[0] >= -Math::EPSILON && weights[1] >= -Math::EPSILON && weights[2] >= -Math::EPSILON;
}
bool Renderer::IsVertexVisible(const Math::Vec4& clipPos)
{
return std::fabs(clipPos.x) <= clipPos.w && std::fabs(clipPos.y) <= clipPos.w && std::fabs(clipPos.z) <= clipPos.w;
}
bool Renderer::IsInsidePlane(const Math::Vec4& clipPos, const Plane& plane)
{
switch (plane)
{
case Plane::POSITIVE_W: return clipPos.w >= 0.0f;
case Plane::POSITIVE_X: return clipPos.x <= +clipPos.w;
case Plane::POSITIVE_Y: return clipPos.y <= +clipPos.w;
case Plane::POSITIVE_Z: return clipPos.z <= +clipPos.w;
case Plane::NEGATIVE_X: return clipPos.x >= -clipPos.w;
case Plane::NEGATIVE_Y: return clipPos.y >= -clipPos.w;
case Plane::NEGATIVE_Z: return clipPos.z >= -clipPos.w;
}
ASSERT(false);
return false;
}
float Renderer::GetIntersectRatio(const Math::Vec4& prev, const Math::Vec4& curr, const Plane plane)
{
switch (plane)
{
case Plane::POSITIVE_W: return (prev.w - 0.0f)/(prev.w - curr.w);
case Plane::POSITIVE_X: return (prev.w - prev.x)/((prev.w - prev.x) - (curr.w - curr.x));
case Plane::NEGATIVE_X: return (prev.w + prev.x)/((prev.w + prev.x) - (curr.w + curr.x));
case Plane::POSITIVE_Y: return (prev.w - prev.y)/((prev.w - prev.y) - (curr.w - curr.y));
case Plane::NEGATIVE_Y: return (prev.w + prev.y)/((prev.w + prev.y) - (curr.w + curr.y));
case Plane::POSITIVE_Z: return (prev.w - prev.z)/((prev.w - prev.z) - (curr.w - curr.z));
case Plane::NEGATIVE_Z: return (prev.w + prev.z)/((prev.w + prev.z) - (curr.w + curr.z));
}
ASSERT(false);
return 0.0f;
}
Renderer::BoundingBox Renderer::GetBoundingBox(const Math::Vec4(& fragCoords)[3], const int width, const int height)
{
auto xList = { fragCoords[0].x, fragCoords[1].x, fragCoords[2].x};
auto yList = { fragCoords[0].y, fragCoords[1].y, fragCoords[2].y};
float minX = std::min<float>(xList);
float maxX = std::max<float>(xList);
float minY = std::min<float>(yList);
float maxY = std::max<float>(yList);
minX = Math::Clamp(minX, 0.0f, (float)(width - 1));
maxX = Math::Clamp(maxX, 0.0f, (float)(width - 1));
minY = Math::Clamp(minY, 0.0f, (float)(height - 1));
maxY = Math::Clamp(maxY, 0.0f, (float)(height - 1));
BoundingBox box{};
box.MinX = std::floor(minX);
box.MinY = std::floor(minY);
box.MaxX = std::ceil(maxX);
box.MaxY = std::ceil(maxY);
return box;
}
bool Renderer::IsBackingFacing(const Math::Vec4& a,const Math::Vec4& b,const Math::Vec4& c)
{
// 逆时针为正
const float signedAera = a.x * b.y - a.y * b.x +
b.x * c.y - b.y * c.x +
c.x * a.y - c.y * a.x;
return signedAera <= 0;
}
bool Renderer::PassDepthTest(const float writeDepth, const float fDepth, const DepthFuncType depthFunc)
{
switch (depthFunc)
{
case DepthFuncType::LESS: return fDepth - writeDepth > Math::EPSILON;
case DepthFuncType::LEQUAL: return fDepth - writeDepth >= -Math::EPSILON;
case DepthFuncType::ALWAYS: return true;
}
ASSERT(false);
return false;
}

View File

@ -0,0 +1,347 @@
//
// Created by sfd on 25-7-28.
//
#ifndef RENDERER_H
#define RENDERER_H
#include <cmath>
#include <cstdint>
#include "FrameBuffer.h"
#include "ShaderBase.h"
template<typename vertex_t>
struct Triangle
{
static_assert(std::is_base_of_v<VertexBase, vertex_t>,"vertex_t 必须继承自 VertexBase");
vertex_t vertex[3];
vertex_t& operator[](size_t i) {return vertex[i];}
const vertex_t& operator[](size_t i) const {return vertex[i];}
Triangle() = default;
};
enum class DepthFuncType
{
LESS,
LEQUAL,
ALWAYS,
};
template<typename vertex_t, typename uniforms_t, typename varyings_t>
struct Program
{
using vertexShader_t = void(*)(varyings_t& , const vertex_t&, const uniforms_t&);
vertexShader_t VertexShader;
using fragmentShader_t = Math::Vec4(*)(bool& discard, const varyings_t&, const uniforms_t&);
fragmentShader_t FragmentShader;
bool EnableDoubleSided = false;
bool EnableDepthTest = true;
bool EnableWriteDepth = true;
bool EnableBlend = true;
DepthFuncType DepthFunc = DepthFuncType::LESS;
explicit Program(const vertexShader_t vertexShader, const fragmentShader_t fragmentShader) : VertexShader(vertexShader), FragmentShader(fragmentShader) {}
};
class Renderer
{
private:
static constexpr int VARYINGS_MAX_COUNT = 9;
enum class Plane
{
POSITIVE_W,
POSITIVE_X,
NEGATIVE_X,
POSITIVE_Y,
NEGATIVE_Y,
POSITIVE_Z,
NEGATIVE_Z,
};
struct BoundingBox {int MinX, MinY, MaxX, MaxY;};
private:
static void CalculateWeights(float(&screenWeights)[3], float(&weights)[3], const Math::Vec4(&fragCoords)[3], const Math::Vec2& screenPoint);
static bool IsInsideTriangle(float (&weights)[3]);
public:
static bool IsVertexVisible(const Math::Vec4& clipPos);
static bool IsInsidePlane(const Math::Vec4& clipPos, const Plane& plane);
static bool IsBackingFacing(const Math::Vec4& a, const Math::Vec4& b,const Math::Vec4& c);
static bool PassDepthTest(float writeDepth, float fDepth, DepthFuncType depthFunc);
static float GetIntersectRatio(const Math::Vec4& prev, const Math::Vec4& curr, Plane plane);
static BoundingBox GetBoundingBox(const Math::Vec4(&fragCoords)[3], int width, int height );
template<typename vertex_t, typename uniforms_t, typename varyings_t>
static void ResterizeTriangle(FrameBuffer& frameBuffer, const Program<vertex_t, uniforms_t, varyings_t>& program, const varyings_t(&varyings)[3], const uniforms_t& uniforms)
{
if (!program.EnableDoubleSided)
{
bool isBackingFacing = false;
isBackingFacing = IsBackingFacing(varyings[0].NDCPos, varyings[1].NDCPos, varyings[2].NDCPos);
if (isBackingFacing) return;
}
Math::Vec4 fragCoords[3];
fragCoords[0] = varyings[0].FragPos;
fragCoords[1] = varyings[1].FragPos;
fragCoords[2] = varyings[2].FragPos;
const Math::Vec2 size = { (float)frameBuffer.GetWidth(), (float)frameBuffer.GetHeight()};
const BoundingBox box = GetBoundingBox(fragCoords, frameBuffer.GetWidth(), frameBuffer.GetHeight());
for (int y = box.MinY; y <= box.MaxY; ++y)
{
for (int x = box.MinX; x <= box.MaxX; ++x)
{
// Varyings setup
float screenWeights[3];
float weights[3];
Math::Vec2 screenPoint{ (float)x + 0.5f, (float)y + 0.5f};
CalculateWeights(screenWeights, weights, fragCoords, screenPoint);
if (!IsInsideTriangle(weights))
continue;
varyings_t pixVaryings;
LerpVaryings(pixVaryings, varyings, weights, size.x, size.y);
// Early depth test
if (program.EnableDepthTest)
{
const float depth = pixVaryings.FragPos.z;
const float fDepth = frameBuffer.GetDepth(x, y);
const DepthFuncType depthFunc = program.DepthFunc;
if (!PassDepthTest(depth, fDepth, depthFunc))
continue;
}
ProcessPixel(frameBuffer, x, y, program, pixVaryings, uniforms);
}
}
}
template<typename varyings_t>
static void CalculateNDCPos(varyings_t(&varyings)[VARYINGS_MAX_COUNT], const int vertexNum)
{
for (int i = 0; i < vertexNum; ++i)
{
float w = varyings[i].ClipPos.w;
varyings[i].NDCPos = varyings[i].ClipPos / w;
varyings[i].NDCPos.w = 1.0f / w;
}
}
template<typename varyings_t>
static void CalculateFragPos(varyings_t(&varyings)[VARYINGS_MAX_COUNT],
const int vertexNum,
const float width,
const float height)
{
for (int i = 0; i < vertexNum; ++i)
{
float x = ((varyings[i].NDCPos.x + 1.0f) * 0.5f * width);
float y = ((varyings[i].NDCPos.y + 1.0f) * 0.5f * height);
float z = ((varyings[i].NDCPos.z + 1.0f) * 0.5f);
float w = varyings[i].NDCPos.w;
// float w = varyings[i].FragPos.w;
varyings[i].FragPos.x = x;
varyings[i].FragPos.y = y;
varyings[i].FragPos.z = z;
varyings[i].FragPos.w = w;
}
}
template <typename varyings_t>
static void LerpVaryings(varyings_t& out, const varyings_t& start, const varyings_t& end, const float ratio)
{
constexpr uint32_t floatNum = sizeof(varyings_t) / sizeof(float);
float* startFloat = (float*)&start;
float* endFloat = (float*)&end;
float* outFloat = (float*)&out;
for (int i = 0; i < floatNum; ++i)
{
outFloat[i] = Math::Lerp(startFloat[i], endFloat[i], ratio);
}
}
template <typename varyings_t>
static void LerpVaryings(varyings_t& out, const varyings_t(&varyings)[3], float(&weigths)[3], const int width, const int height)
{
out.ClipPos = varyings[0].ClipPos * weigths[0] +
varyings[1].ClipPos * weigths[1] +
varyings[2].ClipPos * weigths[2];
out.NDCPos = out.ClipPos / out.ClipPos.w;
out.NDCPos.w = 1.0f / out.ClipPos.w;
out.FragPos.x = (out.NDCPos.x + 1.0f) * 0.5f * width;
out.FragPos.y = (out.NDCPos.y + 1.0f) * 0.5f * height;
out.FragPos.z = (out.NDCPos.z + 1.0f) * 0.5f;
out.FragPos.w = out.NDCPos.w;
constexpr uint32_t floatOffset = sizeof(Math::Vec4)*3 / sizeof(float);
constexpr uint32_t floatNum = sizeof(varyings_t) / sizeof(float);
float* v0 = (float*)&varyings[0];
float* v1 = (float*)&varyings[1];
float* v2 = (float*)&varyings[2];
float* outFloat = (float*)&out;
for (int i = floatOffset; i < floatNum; ++i)
{
outFloat[i] = v0[i] * weigths[0] + v1[i] * weigths[1] + v2[i] * weigths[2];
}
}
template <typename varyings_t>
static int ClipAginstPlane(varyings_t(& outVaryings)[VARYINGS_MAX_COUNT], const varyings_t(& inVaryings)[VARYINGS_MAX_COUNT], const Plane plane,
const int inVertexNum)
{
ASSERT(inVertexNum >= 3);
int outVaryingNum = 0;
for (int i = 0; i < inVertexNum; ++i)
{
int prevIndex = (i - 1 + inVertexNum) % inVertexNum;
int currIndex = i;
const varyings_t& prevVarings = inVaryings[prevIndex];
const varyings_t& currVarings = inVaryings[currIndex];
const bool prevInside = IsInsidePlane(prevVarings.ClipPos, plane);
const bool currInside = IsInsidePlane(currVarings.ClipPos, plane);
if (prevInside != currInside)
{
float ratio = GetIntersectRatio(prevVarings.ClipPos, currVarings.ClipPos, plane);
LerpVaryings(outVaryings[outVaryingNum], prevVarings, currVarings, ratio);
outVaryingNum++;
}
if (currInside)
{
outVaryings[outVaryingNum] = inVaryings[currIndex];
outVaryingNum++;
}
}
ASSERT(outVaryingNum <= VARYINGS_MAX_COUNT);
return outVaryingNum;
}
template <typename varyings_t>
static int Clip(varyings_t(& varyings)[VARYINGS_MAX_COUNT])
{
const bool v0_Visable = IsVertexVisible(varyings[0].ClipPos);
const bool v1_Visable = IsVertexVisible(varyings[1].ClipPos);
const bool v2_Visable = IsVertexVisible(varyings[2].ClipPos);
if (v0_Visable && v1_Visable && v2_Visable) return 3;
int vertexNum = 3;
varyings_t varyings_[VARYINGS_MAX_COUNT];
vertexNum = ClipAginstPlane(varyings_, varyings, Plane::NEGATIVE_Z, vertexNum);
if (vertexNum == 0) return 0;
vertexNum = ClipAginstPlane(varyings, varyings_, Plane::POSITIVE_Z, vertexNum);
if (vertexNum == 0) return 0;
vertexNum = ClipAginstPlane(varyings_, varyings, Plane::NEGATIVE_X, vertexNum);
if (vertexNum == 0) return 0;
vertexNum = ClipAginstPlane(varyings, varyings_, Plane::POSITIVE_X, vertexNum);
if (vertexNum == 0) return 0;
vertexNum = ClipAginstPlane(varyings_, varyings, Plane::NEGATIVE_Y, vertexNum);
if (vertexNum == 0) return 0;
vertexNum = ClipAginstPlane(varyings, varyings_, Plane::POSITIVE_Y, vertexNum);
if (vertexNum == 0) return 0;
return vertexNum;
}
template <typename vertex_t, typename uniform_t, typename varyings_t>
static void Draw(FrameBuffer& frameBuffer, const Program<vertex_t, uniform_t, varyings_t>& program,
const Triangle<vertex_t>& triangle, const uniform_t& uniforms)
{
static_assert(std::is_base_of_v<VertexBase, vertex_t>, "vertex_t 必须继承 BlinnVertex");
static_assert(std::is_base_of_v<VaryingBase, varyings_t>, "varyings_t 必须继承 BlinnVaryings");
// Vertex Shading & Projection
varyings_t varyings[VARYINGS_MAX_COUNT];
for (int i = 0; i < 3; ++i)
{
program.VertexShader(varyings[i], triangle[i], uniforms);
}
// Clipping
int vertexNum = Clip(varyings);
// Screen Mapping
CalculateNDCPos(varyings, vertexNum);
int fWidth = frameBuffer.GetWidth();
int fHeight = frameBuffer.GetHeight();
CalculateFragPos(varyings, vertexNum, (float)fWidth, (float)fHeight);
for (int i = 0; i < vertexNum - 2; ++i)
{
varyings_t triVaryings[3];
triVaryings[0] = varyings[0];
triVaryings[1] = varyings[i + 1];
triVaryings[2] = varyings[i + 2];
ResterizeTriangle(frameBuffer, program, triVaryings, uniforms);
}
}
template <typename vertex_t, typename uniform_t, typename varyings_t>
static void ProcessPixel(FrameBuffer& framebuffer, const int x, const int y, const Program<vertex_t, uniform_t, varyings_t>& program, const varyings_t& varyings, const uniform_t& uniforms)
{
bool discard = false;
Math::Vec4 color{0.0f, 0.0f, 0.0f, 1.0f};
color = program.FragmentShader(discard, varyings, uniforms);
if (discard) return;
color.x = Math::Clamp(color.x , 0.0f, 1.0f);
color.y = Math::Clamp(color.y , 0.0f, 1.0f);
color.z = Math::Clamp(color.z , 0.0f, 1.0f);
color.w = Math::Clamp(color.w , 0.0f, 1.0f);
if (program.EnableBlend)
{
Math::Vec3 dstColor = framebuffer.GetColor(x, y);
Math::Vec3 srcColor = color;
float alpha = color.w;
color = { Math::Lerp(dstColor, srcColor, alpha), 1.0f };
framebuffer.SetColor(x, y, color);
}else
{
framebuffer.SetColor(x, y, color);
}
if (program.EnableWriteDepth)
{
const float depth = varyings.FragPos.z;
framebuffer.SetDepth(x, y, depth);
}
}
};
#endif //RENDERER_H

View File

@ -0,0 +1,5 @@
//
// Created by sfd on 25-7-28.
//
#include "ShaderBase.h"

View File

@ -0,0 +1,32 @@
//
// Created by sfd on 25-7-28.
//
#ifndef SHADERBASE_H
#define SHADERBASE_H
#include "../Math.h"
struct VertexBase
{
Math::Vec4 ModelPosition;
explicit operator const std::string() const { return "ModedPos: " + (std::string)ModelPosition; }
};
struct VaryingBase
{
Math::Vec4 ClipPos { 0.0f, 0.0f, 0.0f, 1.0f};
Math::Vec4 NDCPos { 0.0f, 0.0f, 0.0f, 1.0f};
Math::Vec4 FragPos { 0.0f, 0.0f, 0.0f, 1.0f};
};
struct UniformBase
{
Math::Mat4 MVP;
explicit operator const std::string() const { return "MVP: " + (std::string)MVP; }
};
#endif //SHADERBASE_H

View File

@ -0,0 +1,103 @@
//
// Created by sfd on 25-7-29.
//
#include "Texture.h"
#include <fstream>
#include "../../vendor/stb/stb_image.h"
Texture::Texture(const std::string& filepath)
: m_Filepath(filepath)
{
Init();
}
Texture::~Texture()
{
if (m_Data)
{
delete[] m_Data;
m_Data = nullptr;
}
}
Math::Vec4 Texture::Sample(Math::Vec2 texCoords) const
{
const float vx = Math::Clamp(texCoords.x, 0.0f, 1.0f);
const float vy = Math::Clamp(texCoords.y, 0.0f, 1.0f);
const int x = (m_Width - 1) * vx + 0.5f;
const int y = (m_Height - 1) * vy + 0.5f;
const int index = x + y * m_Width;
return m_Data[index];
}
Texture* Texture::CreateTexture(const std::string& filepath)
{
const std::ifstream file(filepath);
if (file.good())
{
return new Texture(filepath);
}
return nullptr;
}
void Texture::Init()
{
int width, height, channels;
stbi_set_flip_vertically_on_load(1);
const stbi_uc* data = stbi_load(m_Filepath.c_str(), &width, &height, &channels, 0);
ASSERT(data);
m_Width = width;
m_Height = height;
m_Channels = channels;
const int size = width * height;
m_Data = new Math::Vec4[size];
if (channels == 4)
{
for (int i = 0; i < size; i++)
{
m_Data[i].x = Math::UCharToFloat(data[i * 4 + 0]);
m_Data[i].y = Math::UCharToFloat(data[i * 4 + 1]);
m_Data[i].z = Math::UCharToFloat(data[i * 4 + 2]);
m_Data[i].w = Math::UCharToFloat(data[i * 4 + 3]);
}
}else if (channels == 3)
{
for (int i = 0; i < size; i++)
{
m_Data[i].x = Math::UCharToFloat(data[i * 3 + 0]);
m_Data[i].y = Math::UCharToFloat(data[i * 3 + 1]);
m_Data[i].z = Math::UCharToFloat(data[i * 3 + 2]);
m_Data[i].w = 0.0f;
}
}else if (channels == 2)
{
for (int i = 0; i < size; i++)
{
m_Data[i].x = Math::UCharToFloat(data[i * 2 + 0]);
m_Data[i].y = Math::UCharToFloat(data[i * 2 + 1]);
m_Data[i].z = 0.0f;
m_Data[i].w = 0.0f;
}
}else if (channels == 1)
{
for (int i = 0; i < size; i++)
{
m_Data[i].x = Math::UCharToFloat(data[i * 1 + 0]);
m_Data[i].y = 0.0f;
m_Data[i].z = 0.0f;
m_Data[i].w = 0.0f;
}
}
stbi_image_free((void*)data);
}

View File

@ -0,0 +1,34 @@
//
// Created by sfd on 25-7-29.
//
#ifndef TEXTURE_H
#define TEXTURE_H
#include <string>
#include "../Math.h"
class Texture {
public:
Texture(const std::string& filepath);
~Texture();
Math::Vec4 Sample(Math::Vec2 texCoords) const;
static Texture* CreateTexture(const std::string& filepath);
private:
void Init();
private:
int m_Width, m_Height, m_Channels;
std::string m_Filepath;
Math::Vec4* m_Data;
};
#endif //TEXTURE_H

View File

@ -0,0 +1,26 @@
//
// Created by sfd on 25-7-28.
//
#include "Window.h"
#include "../Assert.h"
#include "../../Platform/WindowsWindow.h"
Window::Window(const std::string& name, const int width, const int height)
: m_Name(name), m_Width(width), m_Height(height)
{
ASSERT((m_Width>0) && (m_Height>0));
memset(m_Keys, KEY_RELEASE, sizeof(m_Keys));
}
Window* Window::Create(const std::string& name, const int width, const int height)
{
return new WindowsWindow(name, width, height);
}
void Window::PollInputEvents()
{
WindowsWindow::PollInputEvents();
}

46
src/Core/Window/Window.h Normal file
View File

@ -0,0 +1,46 @@
//
// Created by sfd on 25-7-28.
//
#ifndef WINDOW_H
#define WINDOW_H
#include "../Renderer/FrameBuffer.h"
#include "../Input/InputCodes.h"
#include <string>
#include <cstdint>
class Window {
public:
static Window* Create(const std::string& name, int width, int height);
Window(const std::string& name, int width, int height);
virtual ~Window() = default;
virtual void Show() const = 0;
virtual void DrawFrameBuffer(const FrameBuffer& frameBuffer) = 0;
bool Close() const { return m_Closed; }
inline char GetKey(const uint32_t key) const { return m_Keys[key]; }
public:
virtual void Init() = 0;
virtual void Terminate() = 0;
static void PollInputEvents();
protected:
std::string m_Name;
int m_Width, m_Height;
bool m_Closed = true;
char m_Keys[KEY_MAX_COUNT];
};
#endif //WINDOW_H

View File

@ -0,0 +1,220 @@
//
// Created by sfd on 25-7-28.
//
#include "../Platform/WindowsWindow.h"
#include <windows.h>
#include "../Core/Assert.h"
#define WINDOW_ENTRY_NAME "Entry"
#define WINDOW_CLASS_NAME "Class"
bool WindowsWindow::s_Inited = false;
void WindowsWindow::Init()
{
ASSERT(!s_Inited);
Register();
s_Inited = true;
}
void WindowsWindow::Terminate()
{
ASSERT(s_Inited);
UnRegister();
s_Inited = false;
}
void WindowsWindow::Register()
{
ATOM atom;
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(WHITE_BRUSH); // background Color
wc.hCursor = NULL; // default cursor
wc.hIcon = NULL; // default Icon
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = WindowsWindow::WindowProc; // window process function
wc.lpszClassName = WINDOW_CLASS_NAME;
wc.style = CS_HREDRAW | CS_VREDRAW; // redraw when window resize
wc.lpszMenuName = NULL; // no Menu
atom = RegisterClass(&wc); // register Window
}
void WindowsWindow::UnRegister()
{
UnregisterClass(WINDOW_CLASS_NAME, GetModuleHandle(NULL));
}
void WindowsWindow::KeyPressImpl(WindowsWindow* window, const WPARAM wparam, const char state)
{
if (wparam >= '0' && wparam <= '9')
{
window->m_Keys[wparam] = state;
return;
}
if (wparam >= 'A' && wparam <= 'Z')
{
window->m_Keys[wparam] = state;
return;
}
switch (wparam)
{
case VK_SPACE: window->m_Keys[KEY_SPACE] = state; break;
case VK_SHIFT:
{
window->m_Keys[KEY_LEFT_SHIFT] = state;
window->m_Keys[KEY_RIGHT_SHIFT] = state;
}
break;
case VK_CONTROL:
{
window->m_Keys[KEY_LEFT_CONTROL] = state;
window->m_Keys[KEY_RIGHT_CONTROL] = state;
}
case VK_ESCAPE: window->m_Keys[KEY_ESCAPE] = state; break;
}
}
WindowsWindow::WindowsWindow(const std::string& name, const int width, const int height)
: Window(name, width, height), m_Buffer(nullptr)
{
Init();
ASSERT(s_Inited, "not Init");
DWORD style = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = width;
rect.bottom = height;
AdjustWindowRect(&rect, style, false);
m_hWnd = CreateWindow(
WINDOW_CLASS_NAME,
m_Name.c_str(),
style,
CW_USEDEFAULT,
0,
rect.right - rect.left,
rect.bottom - rect.top,
NULL,
NULL,
GetModuleHandle(NULL),
NULL);
ASSERT(m_hWnd != NULL);
m_Closed = false;
SetProp(m_hWnd, WINDOW_ENTRY_NAME, this);
HDC hdc = GetDC(m_hWnd);
m_MemoryDC = CreateCompatibleDC(hdc);
BITMAPINFOHEADER bitHeader = {};
HBITMAP newBitMap;
HBITMAP oldBitMap;
bitHeader.biSize = sizeof(BITMAPINFOHEADER);
bitHeader.biWidth = ((long)width);
bitHeader.biHeight = -((long)height);
bitHeader.biPlanes = 1;
bitHeader.biBitCount = 24;
bitHeader.biCompression = BI_RGB;
// alloc memory
newBitMap = CreateDIBSection(m_MemoryDC, (BITMAPINFO*)&bitHeader, DIB_RGB_COLORS, (void**)&m_Buffer, nullptr, 0);
ASSERT(newBitMap != NULL);
constexpr int channelCount = 3;
int size = width * height * channelCount * sizeof(unsigned char);
memset(m_Buffer, 0, size);
oldBitMap = (HBITMAP)SelectObject(m_MemoryDC, newBitMap);
DeleteObject(oldBitMap);
ReleaseDC(m_hWnd, m_MemoryDC);
WindowsWindow::Show();
}
WindowsWindow::~WindowsWindow()
{
ShowWindow(m_hWnd, SW_HIDE);
RemoveProp(m_hWnd, WINDOW_ENTRY_NAME);
DeleteDC(m_MemoryDC);
DeleteObject(m_MemoryDC);
}
void WindowsWindow::Show() const
{
HDC windowDC = GetDC(m_hWnd);
BitBlt(windowDC, 0, 0, m_Width, m_Height, m_MemoryDC, 0, 0, SRCCOPY);
ShowWindow(m_hWnd, SW_SHOW);
ReleaseDC(m_hWnd, windowDC);
}
void WindowsWindow::DrawFrameBuffer(const FrameBuffer& frameBuffer)
{
const int fWidth = frameBuffer.GetWidth();
const int fHeight = frameBuffer.GetHeight();
const int width = m_Width < fWidth ? m_Width : fWidth;
const int height = m_Height < fHeight ? m_Height : fHeight;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 反转RGB显示
constexpr int channelCount = 3;
constexpr int rChannel = 2;
constexpr int gChannel = 1;
constexpr int bChannel = 0;
Math::Vec3 color = frameBuffer.GetColor(j, fHeight - i - 1);
const int pixelStart = (i * width + j) * channelCount;
const int rIndex = pixelStart + rChannel;
const int gIndex = pixelStart + gChannel;
const int bIndex = pixelStart + bChannel;
m_Buffer[rIndex] = Math::FloatToUchar(color.r);
m_Buffer[gIndex] = Math::FloatToUchar(color.g);
m_Buffer[bIndex] = Math::FloatToUchar(color.b);
}
}
Show();
}
LRESULT WindowsWindow::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WindowsWindow* window = (WindowsWindow*)GetProp(hWnd, WINDOW_ENTRY_NAME);
if (window == nullptr) return DefWindowProc(hWnd, message, wParam, lParam);
switch (message)
{
case WM_DESTROY: window->m_Closed = true; return 0;
case WM_KEYDOWN: KeyPressImpl(window, wParam, KEY_PRESS); return 0;
case WM_KEYUP: KeyPressImpl(window, wParam, KEY_RELEASE); return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
void WindowsWindow::PollInputEvents()
{
MSG message;
while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
}

View File

@ -0,0 +1,43 @@
//
// Created by sfd on 25-7-28.
//
#ifndef WINDOWSWINDOW_H
#define WINDOWSWINDOW_H
#include <Windows.h>
#include "../Core/Window/Window.h"
class WindowsWindow : public Window{
public:
WindowsWindow(const std::string& name, int width, int height);
virtual ~WindowsWindow();
virtual void Show() const override;
virtual void DrawFrameBuffer(const FrameBuffer& frameBuffer) override;
public:
void Init() override;
void Terminate() override;
static void PollInputEvents();
private:
static void Register();
static void UnRegister();
static void KeyPressImpl(WindowsWindow* window, WPARAM wparam, char state);
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
private:
HWND m_hWnd;
HDC m_MemoryDC;
unsigned char* m_Buffer;
static bool s_Inited;
};
#endif //WINDOWSWINDOW_H

50
src/Shader/DemoShader.cpp Normal file
View File

@ -0,0 +1,50 @@
//
// Created by sfd on 25-7-28.
//
#include "DemoShader.h"
#include <cmath>
void DemoVertexShader(BlinnVaryings& varyings, const BlinnVertex& vertex, const BlinnUniform& uniform)
{
varyings.ClipPos = uniform.MVP * vertex.ModelPosition;
varyings.TexCoord = vertex.TexCoord;
varyings.WorldPos = uniform.Model * vertex.ModelPosition;
varyings.WorldNormal = uniform.ModelNormalToWorld * Math::Vec4{ vertex.ModelNormal, 0.0f};
}
Math::Vec4 DemoFragmentShader(bool& discard, const BlinnVaryings& varyings, const BlinnUniform& uniform)
{
discard = false;
const Math::Vec3 cameraPos = uniform.Camera;
const Math::Vec3 lightPos = uniform.LightPos;
const Math::Vec3 worldPos = varyings.WorldPos;
Math::Vec3 worldNormal = Math::Normalize(varyings.WorldNormal);
const Math::Vec3 viewDir = Math::Normalize(cameraPos - worldPos);
const Math::Vec3 lightDir = Math::Normalize(lightPos - worldPos);
Math::Vec3 halfDir = Math::Normalize(viewDir + lightDir);
Math::Vec3 ambient = uniform.LightAmbient;
Math::Vec3 specularStrength {1.0f, 1.0f, 1.0f};
Math::Vec3 diffColor {1.0f , 1.0f , 1.0f};
if (uniform.Diffuse)
{
const Math::Vec2& texCoord = varyings.TexCoord;
diffColor = uniform.Diffuse->Sample(texCoord);
ambient = ambient * diffColor;
if (uniform.Specular)
specularStrength = uniform.Specular->Sample(texCoord);
}
Math::Vec3 diffuse = std::max(0.0f, Math::Dot(worldNormal, lightDir)) * uniform.LightDiffuse * diffColor;
Math::Vec3 specular = (float)pow(std::max(0.0f, Math::Dot(halfDir, worldNormal)), uniform.Shininess) * uniform.LightSPecular * specularStrength;
Math::Vec3 result = (ambient + diffuse + specular);
return {result, 1.0f};
}

44
src/Shader/DemoShader.h Normal file
View File

@ -0,0 +1,44 @@
//
// Created by sfd on 25-7-28.
//
#ifndef BLINNSHADER_H
#define BLINNSHADER_H
#include "../Core/Renderer/ShaderBase.h"
#include "../Core/Texture/Texture.h"
struct BlinnVertex : public VertexBase
{
Math::Vec3 ModelNormal;
Math::Vec2 TexCoord {0.0f, 0.0f};
};
struct BlinnVaryings : public VaryingBase
{
Math::Vec3 WorldPos;
Math::Vec3 WorldNormal;
Math::Vec2 TexCoord;
};
struct BlinnUniform : public UniformBase
{
Math::Mat4 Model;
Math::Mat4 ModelNormalToWorld;
Math::Vec3 LightPos { 0.0f, 1.0f, 2.0f};
Math::Vec3 LightAmbient { 0.3f, 0.3f, 0.3f};
Math::Vec3 LightDiffuse { 0.5f, 0.5f, 0.5f};
Math::Vec3 LightSPecular { 1.0f, 1.0f, 1.0f};
Math::Vec3 ObjectColor { 1.0f, 1.0f, 1.0f};
Math::Vec3 Camera;
Texture* Diffuse = nullptr;
Texture* Specular = nullptr;
float Shininess = 64.0f;
};
void DemoVertexShader(BlinnVaryings& varyings, const BlinnVertex& vertex, const BlinnUniform& uniform);
Math::Vec4 DemoFragmentShader(bool& discard, const BlinnVaryings& varyings, const BlinnUniform& uniform);
#endif //BLINNSHADER_H

8
src/main.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "Application/Application.h"
int main(const int argc, char **argv)
{
Application app("demo", 400, 400, {argc, argv});
app.Run();
return 0;
}

6
src/vendor/stb/stb_image.cpp vendored Normal file
View File

@ -0,0 +1,6 @@
//
// Created by sfd on 25-7-29.
//
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

7988
src/vendor/stb/stb_image.h vendored Normal file

File diff suppressed because it is too large Load Diff