Compare commits
10 Commits
c5ed219ad2
...
d16fade497
Author | SHA1 | Date |
---|---|---|
Riyyi | d16fade497 | 4 months ago |
Riyyi | b8e4d9ef3f | 4 months ago |
Riyyi | 03e8210165 | 4 months ago |
Riyyi | dcf2a85208 | 4 months ago |
Riyyi | b2ed951b1e | 4 months ago |
Riyyi | d63dfe2f9f | 4 months ago |
Riyyi | faacdca424 | 4 months ago |
Riyyi | b5f3cae5ba | 4 months ago |
Riyyi | 1d5f5a1ad8 | 4 months ago |
Riyyi | 3cd2fab637 | 4 months ago |
46 changed files with 1262 additions and 259 deletions
@ -0,0 +1,19 @@
|
||||
#version 450 core |
||||
|
||||
layout(location = 0) in vec3 a_position; |
||||
layout(location = 1) in vec4 a_color; |
||||
layout(location = 2) in vec2 a_textureCoordinates; |
||||
layout(location = 3) in uint a_textureIndex; |
||||
|
||||
out vec4 v_color; |
||||
out vec2 v_textureCoordinates; |
||||
out flat uint v_textureIndex; |
||||
|
||||
void main() |
||||
{ |
||||
v_color = a_color; |
||||
v_textureCoordinates = a_textureCoordinates; |
||||
v_textureIndex = a_textureIndex; |
||||
// Vclip = Model transform * Vlocal |
||||
gl_Position = vec4(a_position, 1.0f); |
||||
} |
@ -0,0 +1,14 @@
|
||||
#version 450 core |
||||
|
||||
layout(location = 0) out vec4 color; |
||||
|
||||
in vec4 v_color; |
||||
in vec3 v_textureCoordinates; |
||||
in flat uint v_textureIndex; |
||||
|
||||
uniform samplerCube u_textures[32]; |
||||
|
||||
void main() |
||||
{ |
||||
color = v_color; |
||||
} |
@ -0,0 +1,78 @@
|
||||
#version 450 core |
||||
|
||||
layout(location = 0) out vec4 color; |
||||
|
||||
in vec4 v_color; |
||||
in vec2 v_textureCoordinates; |
||||
in flat uint v_textureIndex; |
||||
|
||||
uniform sampler2D u_textures[32]; |
||||
|
||||
// ----------------------------------------- |
||||
|
||||
layout(std140, binding = 0) uniform Camera { |
||||
mat4 u_projectionView; |
||||
vec3 u_position; |
||||
}; |
||||
|
||||
// ----------------------------------------- |
||||
|
||||
struct DirectionalLight { |
||||
vec3 direction; |
||||
|
||||
vec3 ambient; |
||||
vec3 diffuse; |
||||
vec3 specular; |
||||
}; |
||||
|
||||
const int MAX_DIRECTIONAL_LIGHTS = 32; |
||||
layout(std140, binding = 1) uniform DirectionalLights { |
||||
DirectionalLight u_directionalLight[MAX_DIRECTIONAL_LIGHTS]; |
||||
}; |
||||
|
||||
// ----------------------------------------- |
||||
|
||||
void main() |
||||
{ |
||||
float isObject = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).a; |
||||
if (isObject == 0.0f) { |
||||
color = vec4(0,0,0,0); |
||||
return; |
||||
} |
||||
|
||||
vec3 albedo = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).rgb; |
||||
float specular = texture(u_textures[v_textureIndex + 0], v_textureCoordinates).a; |
||||
vec3 position = texture(u_textures[v_textureIndex + 1], v_textureCoordinates).rgb; |
||||
vec3 normal = texture(u_textures[v_textureIndex + 2], v_textureCoordinates).rgb; |
||||
|
||||
|
||||
vec3 lighting = vec3(0.0f, 0.0f, 0.0f);//albedo * v_color.xyz; |
||||
vec3 viewDirection = normalize(u_position - position); |
||||
|
||||
// Loop through all directional lights |
||||
for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; ++i) { |
||||
// Diffuse |
||||
vec3 lightDirection = normalize(-u_directionalLight[i].direction); |
||||
float diffuse = max(dot(normal, lightDirection), 0.0f); |
||||
|
||||
// Specular |
||||
vec3 reflectionDirection = reflect(-lightDirection, normal); |
||||
float specular = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 32); |
||||
|
||||
lighting += |
||||
(albedo * u_directionalLight[i].ambient) + |
||||
(albedo * diffuse * u_directionalLight[i].diffuse) + |
||||
(specular * u_directionalLight[i].specular); |
||||
} |
||||
|
||||
// Loop through all point lights |
||||
// TODO |
||||
// vec3 lightDirection = normalize(lightPosition - position); |
||||
// float diffuse = max(dot(normal, lightDirection), 0.0f); |
||||
// lighting += diffuse * albedo * u_lightColor; |
||||
|
||||
// Loop through all spot lights |
||||
// TODO |
||||
|
||||
color = vec4(lighting, 1.0f); |
||||
} |
@ -0,0 +1,25 @@
|
||||
#version 450 core |
||||
|
||||
layout(location = 0) in vec3 a_position; |
||||
layout(location = 1) in vec4 a_color; |
||||
layout(location = 2) in vec2 a_textureCoordinates; |
||||
layout(location = 3) in uint a_textureIndex; |
||||
|
||||
out vec4 v_color; |
||||
out vec2 v_textureCoordinates; |
||||
out flat uint v_textureIndex; |
||||
|
||||
layout(std140, binding = 0) uniform Camera |
||||
{ |
||||
mat4 u_projectionView; |
||||
vec3 u_position; |
||||
}; |
||||
|
||||
void main() |
||||
{ |
||||
v_color = a_color; |
||||
v_textureCoordinates = a_textureCoordinates; |
||||
v_textureIndex = a_textureIndex; |
||||
// Vclip = Model transform * Vlocal |
||||
gl_Position = vec4(a_position, 1.0f); |
||||
} |
Binary file not shown.
@ -0,0 +1,15 @@
|
||||
# Blender 4.2.0 |
||||
# www.blender.org |
||||
usemtl (null) |
||||
o Plane |
||||
v -1.000000 -1.000000 -0.000000 |
||||
v 1.000000 -1.000000 -0.000000 |
||||
v -1.000000 1.000000 0.000000 |
||||
v 1.000000 1.000000 0.000000 |
||||
vn -0.0000 -0.0000 1.0000 |
||||
vt 0.000000 0.000000 |
||||
vt 1.000000 0.000000 |
||||
vt 1.000000 1.000000 |
||||
vt 0.000000 1.000000 |
||||
s 0 |
||||
f 1/1/1 2/2/1 4/3/1 3/4/1 |
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <cstddef> // size_t |
||||
#include <cstdint> // int32_t, uint32_t, uint8_t |
||||
#include <string_view> |
||||
|
||||
#include "glad/glad.h" |
||||
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2 |
||||
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3 |
||||
#include "glm/gtc/type_ptr.hpp" // glm::value_ptr |
||||
#include "ruc/meta/assert.h" |
||||
|
||||
#include "inferno/render/buffer.h" |
||||
#include "inferno/render/uniformbuffer.h" |
||||
|
||||
namespace Inferno { |
||||
|
||||
Uniformbuffer::Uniformbuffer(s) |
||||
{ |
||||
// Get maximum uniformbuffer bindings the GPU supports
|
||||
int32_t maxBindingPoints = 0; |
||||
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxBindingPoints); |
||||
m_maxBindingPoints = static_cast<uint8_t>(maxBindingPoints); |
||||
} |
||||
|
||||
Uniformbuffer::~Uniformbuffer() |
||||
{ |
||||
for (const auto& [_, block] : m_blocks) { |
||||
glDeleteBuffers(1, &block.id); |
||||
} |
||||
m_blocks.clear(); |
||||
} |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void Uniformbuffer::setLayout(std::string_view blockName, const UniformbufferBlock& block) |
||||
{ |
||||
VERIFY(block.size && block.bindingPoint && block.uniformLocations.size(), |
||||
"invalid uniformbuffer block definition: {}", blockName); |
||||
|
||||
VERIFY(block.bindingPoint < m_maxBindingPoints, |
||||
"uniformbuffer exceeded binding points: {}/{}", block.bindingPoint, m_maxBindingPoints); |
||||
|
||||
m_blocks[blockName] = block; |
||||
} |
||||
|
||||
void Uniformbuffer::setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout) |
||||
{ |
||||
VERIFY(bindingPoint < m_maxBindingPoints, |
||||
"uniformbuffer exceeded binding points: {}/{}", bindingPoint, m_maxBindingPoints); |
||||
|
||||
if (!exists(blockName)) { |
||||
m_blocks[blockName] = {}; |
||||
} |
||||
|
||||
UniformbufferBlock& block = m_blocks[blockName]; |
||||
block.bindingPoint = bindingPoint; |
||||
|
||||
// Example block layout:
|
||||
// - mat3
|
||||
// - float
|
||||
// - vec2
|
||||
// - vec2
|
||||
// - float
|
||||
|
||||
// Chunks, 4 slots, 4 bytes per slot
|
||||
// [x][x][x][ ] #1
|
||||
// [x][x][x][ ] #2
|
||||
// [x][x][x][ ] #3
|
||||
// [x][ ][x][x] #4
|
||||
// [x][x][x][ ] #5
|
||||
|
||||
size_t chunk = 0; |
||||
uint8_t offset = 0; |
||||
|
||||
for (auto it = layout.begin(); it != layout.end(); ++it) { |
||||
BufferElementType type = it->type(); |
||||
const std::string& name = it->name(); |
||||
|
||||
// Calculate offset
|
||||
switch (type) { |
||||
// Scalar 1
|
||||
case BufferElementType::Bool: |
||||
case BufferElementType::Int: |
||||
case BufferElementType::Uint: |
||||
case BufferElementType::Float: { |
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4); |
||||
// Jump
|
||||
offset += 1; |
||||
break; |
||||
} |
||||
// Scalar 2
|
||||
case BufferElementType::Bool2: |
||||
case BufferElementType::Int2: |
||||
case BufferElementType::Uint2: |
||||
case BufferElementType::Vec2: { |
||||
// Add padding
|
||||
if (offset == 1) { |
||||
offset++; |
||||
} |
||||
if (offset == 3) { |
||||
offset = 0; |
||||
chunk++; |
||||
} |
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4); |
||||
// Jump
|
||||
offset += 2; |
||||
break; |
||||
} |
||||
// Scalar 3
|
||||
case BufferElementType::Bool3: |
||||
case BufferElementType::Int3: |
||||
case BufferElementType::Uint3: |
||||
case BufferElementType::Vec3: { |
||||
// Add padding
|
||||
if (offset != 0) { |
||||
offset = 0; |
||||
chunk++; |
||||
} |
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4); |
||||
// Jump
|
||||
offset += 3; |
||||
break; |
||||
} |
||||
// Scalar 4
|
||||
case BufferElementType::Bool4: |
||||
case BufferElementType::Int4: |
||||
case BufferElementType::Uint4: |
||||
case BufferElementType::Vec4: { |
||||
// Add padding
|
||||
if (offset != 0) { |
||||
offset = 0; |
||||
chunk++; |
||||
} |
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4); |
||||
// Jump
|
||||
offset += 4; |
||||
break; |
||||
} |
||||
// Array types
|
||||
case BufferElementType::Mat2: |
||||
case BufferElementType::Mat3: |
||||
case BufferElementType::Mat4: { |
||||
// Add padding
|
||||
if (offset != 0) { |
||||
offset = 0; |
||||
chunk++; |
||||
} |
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4); |
||||
|
||||
// Additional rows
|
||||
if (type == BufferElementType::Mat2) { |
||||
chunk += 1; |
||||
} |
||||
else if (type == BufferElementType::Mat3) { |
||||
chunk += 2; |
||||
} |
||||
else { |
||||
chunk += 3; |
||||
} |
||||
|
||||
// Jump
|
||||
offset += 4; |
||||
break; |
||||
} |
||||
// TODO: Implement these types
|
||||
case BufferElementType::Double: |
||||
case BufferElementType::Vec2Double: |
||||
case BufferElementType::Vec3Double: |
||||
case BufferElementType::Vec4Double: |
||||
case BufferElementType::MatDouble2: |
||||
case BufferElementType::MatDouble3: |
||||
case BufferElementType::MatDouble4: |
||||
VERIFY_NOT_REACHED(); |
||||
case BufferElementType::None: |
||||
VERIFY_NOT_REACHED(); |
||||
}; |
||||
|
||||
// Overflow slots to next chunk
|
||||
if (offset > 3) { |
||||
offset = 0; |
||||
chunk++; |
||||
} |
||||
} |
||||
|
||||
// Pad the end of the buffer
|
||||
if (offset != 0) { |
||||
offset = 0; |
||||
chunk++; |
||||
} |
||||
|
||||
block.size = chunk * 16; |
||||
} |
||||
|
||||
void Uniformbuffer::create(std::string_view blockName) |
||||
{ |
||||
VERIFY(exists(blockName), "uniformbuffer block doesnt exist"); |
||||
|
||||
UniformbufferBlock& block = m_blocks[blockName]; |
||||
|
||||
if (block.id != 0) { |
||||
glDeleteBuffers(1, &block.id); |
||||
} |
||||
|
||||
// Allocate buffer
|
||||
block.id = UINT_MAX; |
||||
glGenBuffers(1, &block.id); |
||||
glBindBuffer(GL_UNIFORM_BUFFER, block.id); |
||||
glBufferData(GL_UNIFORM_BUFFER, block.size, NULL, GL_DYNAMIC_DRAW); |
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0); |
||||
|
||||
// Bind buffer to binding point
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, block.bindingPoint, block.id); |
||||
} |
||||
|
||||
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, bool value) |
||||
{ |
||||
setValue(blockName, member, static_cast<uint32_t>(value), sizeof(uint32_t)); |
||||
} |
||||
|
||||
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, glm::mat2 value) |
||||
{ |
||||
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 2); |
||||
} |
||||
|
||||
void Uniformbuffer::setValue(std::string_view blockName, std::string_view member, glm::mat3 value) |
||||
{ |
||||
setValue(blockName, member, static_cast<glm::mat4>(value), sizeof(glm::vec4) * 3); |
||||
} |
||||
|
||||
} // namespace Inferno
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <cstddef> // size_t |
||||
#include <cstdint> // uint8_t, uint32_t |
||||
#include <string> |
||||
#include <string_view> |
||||
#include <unordered_map> |
||||
|
||||
#include "glad/glad.h" |
||||
#include "glm/ext/matrix_float2x2.hpp" // glm::mat2 |
||||
#include "glm/ext/matrix_float3x3.hpp" // glm::mat3 |
||||
#include "glm/ext/vector_float3.hpp" // glm::vec3 |
||||
#include "ruc/singleton.h" |
||||
|
||||
#include "inferno/render/buffer.h" |
||||
|
||||
#define CHECK_SET_CALL(blockName, member) \ |
||||
VERIFY(exists(blockName), "uniformbuffer block doesnt exist"); \
|
||||
const UniformbufferBlock& block = m_blocks[blockName]; \
|
||||
VERIFY(block.uniformLocations.find(member.data()) != block.uniformLocations.end(), \
|
||||
"uniformbuffer block member doesnt exist"); |
||||
|
||||
namespace Inferno { |
||||
|
||||
// Uniform block layouts, using std140 memory layout rules
|
||||
|
||||
#define MAX_DIRECTIONAL_LIGHTS 32 |
||||
struct UniformDirectionalLight { |
||||
glm::vec3 direction { 0 }; |
||||
float __padding0 { 0 }; |
||||
|
||||
glm::vec3 ambient { 0 }; |
||||
float __padding1 { 0 }; |
||||
glm::vec3 diffuse { 0 }; |
||||
float __padding2 { 0 }; |
||||
glm::vec3 specular { 0 }; |
||||
float __padding3 { 0 }; |
||||
}; |
||||
|
||||
// -----------------------------------------
|
||||
|
||||
struct UniformbufferBlock { |
||||
uint32_t id { 0 }; |
||||
uint32_t size { 0 }; |
||||
uint8_t bindingPoint { 0 }; |
||||
std::unordered_map<std::string, uint32_t> uniformLocations {}; |
||||
}; |
||||
|
||||
class Uniformbuffer final : public ruc::Singleton<Uniformbuffer> { // Uniform Buffer Object, UBO
|
||||
public: |
||||
Uniformbuffer(s); |
||||
~Uniformbuffer(); |
||||
|
||||
void setLayout(std::string_view blockName, const UniformbufferBlock& block); |
||||
void setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout); |
||||
void create(std::string_view blockName); |
||||
|
||||
template<typename T> // Capture value by reference, instead of decaying to pointer
|
||||
void setValue(std::string_view blockName, std::string_view member, T&& value, size_t size = 0) |
||||
{ |
||||
CHECK_SET_CALL(blockName, member); |
||||
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, block.id); |
||||
glBufferSubData(GL_UNIFORM_BUFFER, block.uniformLocations.at(member.data()), (size) ? size : sizeof(T), &value); |
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0); |
||||
} |
||||
// Exceptions:
|
||||
void setValue(std::string_view blockName, std::string_view member, bool value); |
||||
void setValue(std::string_view blockName, std::string_view member, glm::mat2 value); |
||||
void setValue(std::string_view blockName, std::string_view member, glm::mat3 value); |
||||
|
||||
bool exists(std::string_view blockName) const { return m_blocks.find(blockName) != m_blocks.end(); } |
||||
|
||||
private: |
||||
uint8_t m_maxBindingPoints { 0 }; |
||||
std::unordered_map<std::string_view, UniformbufferBlock> m_blocks; |
||||
}; |
||||
|
||||
} // namespace Inferno
|
||||
|
||||
#if 0 |
||||
|
||||
// -----------------------------------------
|
||||
// Example usage:
|
||||
|
||||
Uniformbuffer::the().setLayout( |
||||
"ExampleBlock", 0, |
||||
{ |
||||
{ BufferElementType::Mat3, "a" }, |
||||
{ BufferElementType::Float, "b" }, |
||||
{ BufferElementType::Vec2, "c" }, |
||||
{ BufferElementType::Vec2, "d" }, |
||||
{ BufferElementType::Float, "e" }, |
||||
}); |
||||
Uniformbuffer::the().create("ExampleBlock"); |
||||
|
||||
#endif |
||||
|
||||
// -----------------------------------------
|
||||
// Memory alignment of uniform blocks using std140
|
||||
//
|
||||
// Main points:
|
||||
// - Memory is organized into chunks.
|
||||
// - A block is at least the size of 1 chunk.
|
||||
// - One chunk has 4 slots, 4 bytes per slot.
|
||||
// - Can't fit? Move to next chunk.
|
||||
//
|
||||
// The rules:
|
||||
// 1. Scalar (bool, int, uint, float) takes up 1 slot, can appear after anything
|
||||
// 2. Vec2 takes up 2 slots, in first or last half of a chunk
|
||||
// 3. Vec3 takes up 3 slots, only at the start of a chunk
|
||||
// 4. Everything else:
|
||||
// - Take up maxumum room
|
||||
// - As often as needed
|
||||
// - Add padding as needed
|
||||
// 5. Mat3 (or any matrix) are treated like arrays
|
||||
// 6. Each member of *any* array gets its own chunk
|
||||
|
||||
// TODO: How do double types work?
|
||||
|
||||
// -----------------------------------------
|
||||
// References:
|
||||
// - https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
|
||||
// - https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout
|
||||
// - https://www.oreilly.com/library/view/opengl-programming-guide/9780132748445/app09lev1sec2.html (The std140 Layout Rules)
|
||||
// - https://www.youtube.com/watch?v=JPvbRko9lBg (WebGL 2: Uniform Buffer Objects)
|
Loading…
Reference in new issue