한 3시간 걸린 것 같습니다.
재밌네요
전체코드
더보기
#include <iostream>
#define NOMINMAX
#include <Windows.h>
#include <cassert>
#include <chrono>
#include <vector>
#define PI 3.14159265358979323846f
#define DEG2RAD(x) ((x) * PI / 180.f)
struct ScreenBuffer
{
uint8_t* buffer;
uint32_t width;
uint32_t height;
};
struct Matrix4x4
{
float m[4][4];
static Matrix4x4 Identity()
{
Matrix4x4 result = {};
result.m[0][0] = 1.0f;
result.m[1][1] = 1.0f;
result.m[2][2] = 1.0f;
result.m[3][3] = 1.0f;
return result;
}
};
struct Vec3
{
Vec3 operator- (const Vec3& other) const { return { x - other.x, y - other.y, z - other.z }; }
Vec3 operator*(const float& scalar) const { return { x * scalar, y * scalar, z * scalar }; }
Vec3 operator+(const Vec3& other) const { return { x + other.x, y + other.y, z + other.z }; }
float Dot(const Vec3& other) const { return x * other.x + y * other.y + z * other.z; }
Vec3 Cross(const Vec3& other) const
{
return {
y * other.z - z * other.y,
z * other.x - x * other.z,
x * other.y - y * other.x
};
}
Vec3 Normalize() const
{
float length = sqrtf(x * x + y * y + z * z);
return { x / length, y / length, z / length };
}
float x, y, z;
};
struct Vec4
{
Vec4() = default;
Vec4(const Vec3& vec3, float w) : x(vec3.x), y(vec3.y), z(vec3.z), w(w) {}
Vec3 ToVec3() const { return Vec3{ x,y,z }; }
float x, y, z, w;
};
struct GrayScale
{
float scale;
};
struct Vertex
{
Vec3 posL;
Vec3 normal;
GrayScale color;
};
struct PSInput
{
Vec4 posH;
Vec3 posW;
Vec3 normal;
GrayScale color;
};
struct Context
{
ScreenBuffer frontBuffer;
float* depthBuffer;
HANDLE mainConsole;
bool prevKeyInput[256];
bool keyInput[256];
float deltaTime;
std::chrono::high_resolution_clock::time_point lastTime;
PSInput(*vertexShader)(const Vertex&);
GrayScale(*pixelShader)(const PSInput&);
} g_context;
void InitContext(uint32_t width, uint32_t height);
void ReleaseContext();
void DrawPixel(const ScreenBuffer* in_buffer, uint32_t x, uint32_t y, uint8_t color);
void PresentToConsole(const ScreenBuffer* in_buffer);
char CvtColorToChar(uint8_t color);
void ClearScreenBuffer(const ScreenBuffer* in_buffer, uint8_t color);
void InitContext(uint32_t width, uint32_t height)
{
g_context.mainConsole = GetStdHandle(STD_OUTPUT_HANDLE);
g_context.frontBuffer.width = width;
g_context.frontBuffer.height = height;
g_context.frontBuffer.buffer = new uint8_t[width * height];
g_context.depthBuffer = new float[width * height];
g_context.lastTime = std::chrono::high_resolution_clock::now();
ClearScreenBuffer(&g_context.frontBuffer, 0);
PresentToConsole(&g_context.frontBuffer);
}
void ReleaseContext()
{
delete[] g_context.frontBuffer.buffer;
delete[] g_context.depthBuffer;
}
char CvtColorToChar(uint8_t color)
{
uint8_t key = (color / 255.f) * 9.f;
switch (key)
{
case 0: return ' ';
case 1: return '.';
case 2: return ':';
case 3: return '-';
case 4: return '=';
case 5: return '+';
case 6: return '*';
case 7: return '#';
case 8: return '%';
case 9: return '@';
default: return ' ';
}
}
void DrawPixel(const ScreenBuffer* in_buffer, uint32_t x, uint32_t y, uint8_t color)
{
if (x < in_buffer->width && y < in_buffer->height)
in_buffer->buffer[y * in_buffer->width + x] = CvtColorToChar(color);
}
void PresentToConsole(const ScreenBuffer* in_buffer)
{
DWORD written;
for (int y = 0; y < in_buffer->height; y++)
{
WriteConsoleA(g_context.mainConsole,
in_buffer->buffer + y * in_buffer->width,
in_buffer->width,
&written, nullptr);
WriteConsoleA(g_context.mainConsole,
"\n",
1,
&written, nullptr);
}
SetConsoleCursorPosition(g_context.mainConsole, { 0, 0 });
}
void ClearScreenBuffer(const ScreenBuffer* in_buffer, uint8_t color)
{
std::memset(in_buffer->buffer, CvtColorToChar(color), in_buffer->width * in_buffer->height);
}
void ClearDepthBuffer(const ScreenBuffer* in_buffer, float depth)
{
std::memset(g_context.depthBuffer, depth, in_buffer->width * in_buffer->height * sizeof(float));
}
bool DepthTest(float depth, uint32_t x, uint32_t y)
{
if (depth > g_context.depthBuffer[y * g_context.frontBuffer.width + x])
{
g_context.depthBuffer[y * g_context.frontBuffer.width + x] = depth;
return true;
}
return false;
}
void UpdateInput()
{
for (int i = 0; i < 256; i++)
{
if (GetAsyncKeyState(i) & 0x8000)
g_context.keyInput[i] = true;
else
g_context.keyInput[i] = false;
}
std::memcpy(g_context.prevKeyInput, g_context.keyInput, sizeof(g_context.prevKeyInput));
}
void UpdateTimer()
{
auto now = std::chrono::high_resolution_clock::now();
g_context.deltaTime = std::chrono::duration<float>(now - g_context.lastTime).count();
g_context.lastTime = now;
}
bool IsKeyPressed(int key) { return g_context.keyInput[key] && !g_context.prevKeyInput[key]; }
bool IsKeyReleased(int key) { return !g_context.keyInput[key] && g_context.prevKeyInput[key]; }
bool IsKeyDown(int key) { return g_context.keyInput[key]; }
bool IsKeyUp(int key) { return !g_context.keyInput[key]; }
Matrix4x4 Multiply(const Matrix4x4& a, const Matrix4x4& b)
{
Matrix4x4 result;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
result.m[i][j] = a.m[i][0] * b.m[0][j] +
a.m[i][1] * b.m[1][j] +
a.m[i][2] * b.m[2][j] +
a.m[i][3] * b.m[3][j];
}
}
return result;
}
Matrix4x4 Invert(const Matrix4x4& mat)
{
Matrix4x4 inv;
float det = 0.0f;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
inv.m[i][j] = mat.m[j][i];
}
}
det = mat.m[0][0] * inv.m[1][1] * inv.m[2][2] * inv.m[3][3] +
mat.m[0][1] * inv.m[1][2] * inv.m[2][3] * inv.m[3][0] +
mat.m[0][2] * inv.m[1][3] * inv.m[2][0] * inv.m[3][1] +
mat.m[0][3] * inv.m[1][0] * inv.m[2][1] * inv.m[3][2];
return inv;
}
Matrix4x4 Transpose(const Matrix4x4& mat)
{
Matrix4x4 result;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
result.m[i][j] = mat.m[j][i];
}
return result;
}
Vec4 TransformVector(const Matrix4x4& mat, const Vec4& vec)
{
Vec4 result;
result.x = mat.m[0][0] * vec.x + mat.m[0][1] * vec.y + mat.m[0][2] * vec.z + mat.m[0][3] * vec.w;
result.y = mat.m[1][0] * vec.x + mat.m[1][1] * vec.y + mat.m[1][2] * vec.z + mat.m[1][3] * vec.w;
result.z = mat.m[2][0] * vec.x + mat.m[2][1] * vec.y + mat.m[2][2] * vec.z + mat.m[2][3] * vec.w;
result.w = mat.m[3][0] * vec.x + mat.m[3][1] * vec.y + mat.m[3][2] * vec.z + mat.m[3][3] * vec.w;
return result;
}
Vec4 TransformVector(const Matrix4x4& mat, const Vec3& vec)
{
Vec4 result;
result.x = mat.m[0][0] * vec.x + mat.m[0][1] * vec.y + mat.m[0][2] * vec.z + mat.m[0][3] * 1.f;
result.y = mat.m[1][0] * vec.x + mat.m[1][1] * vec.y + mat.m[1][2] * vec.z + mat.m[1][3] * 1.f;
result.z = mat.m[2][0] * vec.x + mat.m[2][1] * vec.y + mat.m[2][2] * vec.z + mat.m[2][3] * 1.f;
result.w = mat.m[3][0] * vec.x + mat.m[3][1] * vec.y + mat.m[3][2] * vec.z + mat.m[3][3] * 1.f;
return result;
}
Matrix4x4 CreateWorld(const Vec3& translate, const Vec3& yawPitchRoll, const Vec3& scale)
{
Matrix4x4 world;
float cy = cos(yawPitchRoll.y);
float sy = sin(yawPitchRoll.y);
float cp = cos(yawPitchRoll.x);
float sp = sin(yawPitchRoll.x);
float cr = cos(yawPitchRoll.z);
float sr = sin(yawPitchRoll.z);
Matrix4x4 rotationYaw = {
cy, 0.0f, sy, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-sy, 0.0f, cy, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
Matrix4x4 rotationPitch = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, cp, sp, 0.0f,
0.0f, -sp, cp, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
Matrix4x4 rotationRoll = {
cr, sr, 0.0f, 0.0f,
-sr, cr, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
Matrix4x4 rotation = Multiply(rotationYaw, Multiply(rotationPitch, rotationRoll));
world.m[0][0] = scale.x * rotation.m[0][0];
world.m[0][1] = scale.x * rotation.m[0][1];
world.m[0][2] = scale.x * rotation.m[0][2];
world.m[0][3] = translate.x;
world.m[1][0] = scale.y * rotation.m[1][0];
world.m[1][1] = scale.y * rotation.m[1][1];
world.m[1][2] = scale.y * rotation.m[1][2];
world.m[1][3] = translate.y;
world.m[2][0] = scale.z * rotation.m[2][0];
world.m[2][1] = scale.z * rotation.m[2][1];
world.m[2][2] = scale.z * rotation.m[2][2];
world.m[2][3] = translate.z;
world.m[3][0] = 0.0f;
world.m[3][1] = 0.0f;
world.m[3][2] = 0.0f;
world.m[3][3] = 1.0f;
return world;
}
Matrix4x4 CreateView(const Vec3& cameraPosition, const Vec3& lookDirection)
{
Vec3 zAxis = (lookDirection * -1.0f).Normalize();
Vec3 up = { 0.0f, 1.0f, 0.0f };
if (dot > 0.999f)
{
up = { 0.0f, 0.0f, 1.0f };
{
up = { 1.0f, 0.0f, 0.0f };
}
}
Vec3 xAxis = up.Cross(zAxis).Normalize();
Vec3 yAxis = zAxis.Cross(xAxis).Normalize();
Matrix4x4 view = {
xAxis.x, xAxis.y, xAxis.z, -xAxis.Dot(cameraPosition),
yAxis.x, yAxis.y, yAxis.z, -yAxis.Dot(cameraPosition),
zAxis.x, zAxis.y, zAxis.z, -zAxis.Dot(cameraPosition),
0.0f, 0.0f, 0.0f, 1.0f
};
return view;
}
Matrix4x4 CreateProjection(float fov, float aspect, float nearZ, float farZ)
{
float f = 1.0f / tanf(fov * 0.5f);
float rangeInv = 1.0f / (nearZ - farZ);
Matrix4x4 projection = {
f / aspect, 0.0f, 0.0f, 0.0f,
0.0f, f, 0.0f, 0.0f,
0.0f, 0.0f, (nearZ + farZ) * rangeInv, -1.0f,
0.0f, 0.0f, (2 * nearZ * farZ) * rangeInv, 0.0f
};
return projection;
}
void SetVertexShader(PSInput(*vertexShader)(const Vertex&))
{
g_context.vertexShader = vertexShader;
}
void SetPixelShader(GrayScale(*pixelShader)(const PSInput&))
{
g_context.pixelShader = pixelShader;
}
void Render(const Vertex* vertices, uint32_t vertexCount,
const uint32_t* indices, uint32_t indexCount)
{
assert(g_context.vertexShader && "Vertex shader not set");
assert(g_context.pixelShader && "Pixel shader not set");
std::vector<PSInput> rasterizerInput;
rasterizerInput.reserve(vertexCount);
// vertex shader
for (uint32_t i = 0; i < vertexCount; i++)
{
Vertex vertex = vertices[i];
PSInput psInput = g_context.vertexShader(vertex);
// perspective divide
psInput.posH.x /= psInput.posH.w;
psInput.posH.y /= psInput.posH.w;
psInput.posH.z /= psInput.posH.w;
psInput.posH.w = 1.0f;
// viewport transformation
psInput.posH.x = (psInput.posH.x + 1.0f) * 0.5f * g_context.frontBuffer.width;
psInput.posH.y = (-psInput.posH.y + 1.0f) * 0.5f * g_context.frontBuffer.height;
rasterizerInput.emplace_back(psInput);
}
// rasterization
for (size_t i = 0; i < indexCount / 3; ++i)
{
PSInput psInput1 = rasterizerInput[indices[i * 3]];
PSInput psInput2 = rasterizerInput[indices[i * 3 + 1]];
PSInput psInput3 = rasterizerInput[indices[i * 3 + 2]];
// back face culling
Vec3 edge1 = psInput2.posW - psInput1.posW;
Vec3 edge2 = psInput3.posW - psInput1.posW;
Vec3 normal = edge2.Cross(edge1);
if (normal.z > 0.0f)
continue;
// bounding box
int minX = std::min({ psInput1.posH.x, psInput2.posH.x, psInput3.posH.x });
int maxX = std::max({ psInput1.posH.x, psInput2.posH.x, psInput3.posH.x });
int minY = std::min({ psInput1.posH.y, psInput2.posH.y, psInput3.posH.y });
int maxY = std::max({ psInput1.posH.y, psInput2.posH.y, psInput3.posH.y });
minX = std::max(minX, 0);
maxX = std::min(maxX, static_cast<int>(g_context.frontBuffer.width) - 1);
minY = std::max(minY, 0);
maxY = std::min(maxY, static_cast<int>(g_context.frontBuffer.height) - 1);
// triangle rasterization
for (int y = minY; y <= maxY; ++y)
{
for (int x = minX; x <= maxX; ++x)
{
// barycentric coordinates
float alpha = ((psInput2.posH.y - psInput3.posH.y) * (x - psInput3.posH.x) +
(psInput3.posH.x - psInput2.posH.x) * (y - psInput3.posH.y)) /
((psInput2.posH.y - psInput3.posH.y) * (psInput1.posH.x - psInput3.posH.x) +
(psInput3.posH.x - psInput2.posH.x) * (psInput1.posH.y - psInput3.posH.y));
float beta = ((psInput3.posH.y - psInput1.posH.y) * (x - psInput3.posH.x) +
(psInput1.posH.x - psInput3.posH.x) * (y - psInput3.posH.y)) /
((psInput2.posH.y - psInput3.posH.y) * (psInput1.posH.x - psInput3.posH.x) +
(psInput3.posH.x - psInput2.posH.x) * (psInput1.posH.y - psInput3.posH.y));
float gamma = 1.0f - alpha - beta;
if (alpha >= 0 && beta >= 0 && gamma >= 0)
{
// interpolate color
uint8_t color = static_cast<uint8_t>(alpha * psInput1.color.scale +
beta * psInput2.color.scale +
gamma * psInput3.color.scale);
float z = alpha * psInput1.posH.z +
beta * psInput2.posH.z +
gamma * psInput3.posH.z;
if (!DepthTest(z, x, y))
continue;
PSInput input;
input.posH.x = x;
input.posH.y = y;
input.posH.z = z;
input.posH.w = 1.0f;
input.color.scale = color;
input.normal = psInput1.normal * alpha +
psInput2.normal * beta +
psInput3.normal * gamma;
input.posW = psInput1.posW * alpha +
psInput2.posW * beta +
psInput3.posW * gamma;
// pixel shader
GrayScale output = g_context.pixelShader(input);
uint8_t grayScale = output.scale * 255.f;
DrawPixel(&g_context.frontBuffer, x, y, grayScale);
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
// cube
Vertex vertices[36] = {
// front
{ { -1.f, -1.f, 1.f }, { 0.0f, 0.0f, 1.0f }, { 1.f } },
{ { 1.f, -1.f, 1.f }, { 0.0f, 0.0f, 1.0f }, { 1.f } },
{ { 1.f, 1.f, 1.f }, { 0.0f, 0.0f, 1.0f }, { 1.f } },
{ { -1.f, 1.f, 1.f }, { 0.0f, 0.0f, 1.0f }, { 1.f } },
// right
{ { 1.f, -1.f, 1.f }, { 1.0f, 0.0f, 0.0f }, { 1.f } },
{ { 1.f, -1.f, -1.f }, { 1.0f, 0.0f, 0.0f }, { 1.f } },
{ { 1.f, 1.f, -1.f }, { 1.0f, 0.0f, 0.0f }, { 1.f } },
{ { 1.f, 1.f, 1.f }, { 1.0f, 0.0f, 0.0f }, { 1.f } },
// top
{ { -1.f, 1.f, 1.f }, { 0.0f, 1.0f, 0.0f }, { 1.f } },
{ { 1.f, 1.f, 1.f }, { 0.0f, 1.0f, 0.0f }, { 1.f } },
{ { 1.f, 1.f, -1.f }, { 0.0f, 1.0f, 0.0f }, { 1.f } },
{ { -1.f, 1.f, -1.f }, { 0.0f, 1.0f, 0.0f }, { 1.f } },
// back
{ { 1.f, -1.f, -1.f }, { 0.0f, 0.0f, -1.0f }, { 1.f } },
{ { -1.f, -1.f, -1.f }, { 0.0f, 0.0f, -1.0f }, { 1.f } },
{ { -1.f, 1.f, -1.f }, { 0.0f, 0.0f, -1.0f }, { 1.f } },
{ { 1.f, 1.f, -1.f }, { 0.0f, 0.0f, -1.0f }, { 1.f } },
// left
{ { -1.f, -1.f, -1.f }, { -1.0f, 0.0f, 0.0f }, { 1.f } },
{ { -1.f, -1.f, 1.f }, { -1.0f, 0.0f, 0.0f }, { 1.f } },
{ { -1.f, 1.f, 1.f }, { -1.0f, 0.0f, 0.0f }, { 1.f } },
{ { -1.f, 1.f, -1.f }, { -1.0f, 0.0f, 0.0f }, { 1.f } },
// bottom
{ { -1.f, -1.f, -1.f }, { 0.0f, -1.0f, 0.0f }, { 1.f } },
{ { 1.f, -1.f, -1.f }, { 0.0f, -1.0f, 0.0f }, { 1.f } },
{ { 1.f, -1.f, 1.f }, { 0.0f, -1.0f, 0.0f }, { 1.f } },
{ { -1.f, -1.f, 1.f }, { 0.0f, -1.0f, 0.0f }, { 1.f } }
};
uint32_t indices[36] = {
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23
};
struct Transform
{
Vec3 translate;
Vec3 yawPitchRoll;
Vec3 scale;
} g_transform = { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f } };
Transform g_cameraTransform = { { 0.0f, 0.0f, 15.f }, { 0.0f, 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f } };
struct Uniforms
{
Matrix4x4 worldMatrix;
Matrix4x4 worldInverseTransposeMatrix;
Matrix4x4 viewMatrix;
Matrix4x4 projectionMatrix;
Vec3 lightPosition;
Vec3 cameraPosition;
} g_uniforms;
PSInput VertexShader(const Vertex& vertex)
{
Vec3 posW = TransformVector(g_uniforms.worldMatrix, vertex.posL).ToVec3();
Vec3 normalW = TransformVector(g_uniforms.worldInverseTransposeMatrix, vertex.normal).ToVec3();
Matrix4x4 projViewMatrix = Multiply(g_uniforms.projectionMatrix, g_uniforms.viewMatrix);
Vec4 posP = TransformVector(projViewMatrix, posW);
PSInput input;
input.posH = posP;
input.posW = posW;
input.normal = normalW;
input.color = vertex.color;
return input;
}
GrayScale PixelShader(const PSInput& input)
{
GrayScale output;
Vec3 normal = input.normal.Normalize();
Vec3 lightDir = (g_uniforms.lightPosition - input.posW).Normalize();
Vec3 toEye = (g_uniforms.cameraPosition - input.posW).Normalize();
Vec3 halfVector = (lightDir + toEye).Normalize();
float ambient = 0.2f;
output.scale = ambient + diffuse;
output.scale = std::max(output.scale, 0.0f);
output.scale = std::min(output.scale, 1.0f);
return output;
}
int main()
{
InitContext(240, 80);
g_uniforms.lightPosition = { 5.0f, 5.0f, 5.0f };
SetVertexShader(VertexShader);
SetPixelShader(PixelShader);
while (true)
{
UpdateInput();
UpdateTimer();
if (IsKeyDown(VK_ESCAPE))
break;
ClearScreenBuffer(&g_context.frontBuffer, 0);
ClearDepthBuffer(&g_context.frontBuffer, 0.f);
// Translate
if (IsKeyDown(VK_LEFT))
g_transform.translate.x -= 1.0f * g_context.deltaTime;
if (IsKeyDown(VK_RIGHT))
g_transform.translate.x += 1.0f * g_context.deltaTime;
if (IsKeyDown(VK_UP))
g_transform.translate.y += 1.0f * g_context.deltaTime;
if (IsKeyDown(VK_DOWN))
g_transform.translate.y -= 1.0f * g_context.deltaTime;
// Rotate
if (IsKeyDown('A'))
g_transform.yawPitchRoll.y -= 1.0f * g_context.deltaTime;
if (IsKeyDown('D'))
g_transform.yawPitchRoll.y += 1.0f * g_context.deltaTime;
if (IsKeyDown('W'))
g_transform.yawPitchRoll.x -= 1.0f * g_context.deltaTime;
if (IsKeyDown('S'))
g_transform.yawPitchRoll.x += 1.0f * g_context.deltaTime;
// Scale
if (IsKeyDown('Q'))
{
g_transform.scale.x -= 1.0f * g_context.deltaTime;
g_transform.scale.y -= 1.0f * g_context.deltaTime;
}
if (IsKeyDown('E'))
{
g_transform.scale.x += 1.0f * g_context.deltaTime;
g_transform.scale.y += 1.0f * g_context.deltaTime;
}
// camera position
if (IsKeyDown('Z'))
g_cameraTransform.translate.z -= 1.0f * g_context.deltaTime;
if (IsKeyDown('X'))
g_cameraTransform.translate.z += 1.0f * g_context.deltaTime;
// light position
if (IsKeyDown('I'))
g_uniforms.lightPosition.z -= 1.0f * g_context.deltaTime;
if (IsKeyDown('K'))
g_uniforms.lightPosition.z += 1.0f * g_context.deltaTime;
if (IsKeyDown('J'))
g_uniforms.lightPosition.x -= 1.0f * g_context.deltaTime;
if (IsKeyDown('L'))
g_uniforms.lightPosition.x += 1.0f * g_context.deltaTime;
if (IsKeyDown('U'))
g_uniforms.lightPosition.y -= 1.0f * g_context.deltaTime;
if (IsKeyDown('O'))
g_uniforms.lightPosition.y += 1.0f * g_context.deltaTime;
g_uniforms.worldMatrix = CreateWorld(g_transform.translate, g_transform.yawPitchRoll, g_transform.scale);
g_uniforms.worldInverseTransposeMatrix = Invert(Transpose(g_uniforms.worldMatrix));
g_uniforms.viewMatrix = CreateView(g_cameraTransform.translate, { 0.0f, 0.0f, -1.0f });
g_uniforms.projectionMatrix = CreateProjection(DEG2RAD(60.f),
1.66f, 0.1f, 100.f);
g_uniforms.cameraPosition = g_cameraTransform.translate;
Render(vertices, std::size(vertices), indices, std::size(indices));
PresentToConsole(&g_context.frontBuffer);
}
ReleaseContext();
}