Autogenerated surface shaders

This commit is contained in:
tovjemam 2026-05-23 18:28:22 +02:00
parent 308c39b8f8
commit 3bb458e0ab
11 changed files with 417 additions and 412 deletions

View File

@ -105,11 +105,15 @@ set(CLIENT_ONLY_SOURCES
"src/gfx/frustum.cpp"
"src/gfx/renderer.hpp"
"src/gfx/renderer.cpp"
"src/gfx/shader_common.hpp"
"src/gfx/shader_defs.hpp"
"src/gfx/shader_sources.hpp"
"src/gfx/shader_sources.cpp"
"src/gfx/shader.hpp"
"src/gfx/shader.cpp"
"src/gfx/surface_render_flags.hpp"
"src/gfx/surface_shader.hpp"
"src/gfx/surface_shader.cpp"
"src/gfx/surface.hpp"
"src/gfx/texture.cpp"
"src/gfx/texture.hpp"

View File

@ -5,6 +5,7 @@
#include "assets/skeleton.hpp"
#include "surface.hpp"
#include "uniform_buffer.hpp"
#include "surface_render_flags.hpp"
namespace gfx
{
@ -18,6 +19,7 @@ struct DrawSurfaceCmd
uint32_t count = 0; // num triangles
float dist = 0.0f; // distance to camera - for transparnt sorting
const UniformBuffer<glm::mat4>* skinning = nullptr; // skinning matrices for skeletal meshes
SurfaceRenderFlags rflags = 0;
};
struct DrawBeamCmd

View File

@ -14,9 +14,6 @@
gfx::Renderer::Renderer()
{
ShaderSources::MakeShader(mesh_shader_.shader, SS_MESH_VERT, SS_MESH_FRAG);
ShaderSources::MakeShader(skel_mesh_shader_.shader, SS_SKEL_MESH_VERT, SS_SKEL_MESH_FRAG);
ShaderSources::MakeShader(deform_mesh_shader_.shader, SS_DEFORM_MESH_VERT, SS_DEFORM_MESH_FRAG);
ShaderSources::MakeShader(solid_shader_, SS_SOLID_VERT, SS_SOLID_FRAG);
ShaderSources::MakeShader(hud_shader_, SS_HUD_VERT, SS_HUD_FRAG);
ShaderSources::MakeShader(beam_shader_, SS_BEAM_VERT, SS_BEAM_FRAG);
@ -89,20 +86,32 @@ void gfx::Renderer::SetupBeamVA()
void gfx::Renderer::InvalidateShaders()
{
InvalidateMeshShader(mesh_shader_);
InvalidateMeshShader(skel_mesh_shader_);
InvalidateMeshShader(deform_mesh_shader_);
// invalidate surface shaders
for (auto& [flags, sshader] : surface_shaders_)
{
InvalidateSurfaceShader(sshader);
}
}
void gfx::Renderer::InvalidateMeshShader(MeshShader& mshader)
gfx::SurfaceShader& gfx::Renderer::GetSurfaceShader(SurfaceRenderFlags flags)
{
mshader.global_setup = false;
mshader.color = glm::vec4(-1.0f); // invalidate color
auto it = surface_shaders_.find(flags);
// not yet generated
if (it == surface_shaders_.end())
{
SurfaceShader& sshader = surface_shaders_[flags];
sshader.shader = CreateSurfaceShader(flags, sshader.iflags);
return sshader;
}
return it->second;
}
void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& params)
void gfx::Renderer::SetupSurfaceShader(SurfaceShader& sshader, const DrawListParams& params)
{
const Shader& shader = *mshader.shader;
const Shader& shader = *sshader.shader;
if (current_shader_ != &shader)
{
@ -110,7 +119,7 @@ void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& p
current_shader_ = &shader;
}
if (mshader.global_setup)
if (sshader.global_setup)
{
return; // Global uniforms are already set up
}
@ -118,23 +127,65 @@ void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& p
glUniformMatrix4fv(shader.U(gfx::SU_VIEW_PROJ), 1, GL_FALSE, &params.view_proj[0][0]);
// setup lighting
glUniform3fv(shader.U(gfx::SU_AMBIENT_LIGHT), 1, &params.env.ambient_light[0]);
glUniform3fv(shader.U(gfx::SU_SUN_COLOR), 1, &params.env.sun_color[0]);
glUniform3fv(shader.U(gfx::SU_SUN_DIRECTION), 1, &params.env.sun_direction[0]);
glUniform4fv(shader.U(gfx::SU_FOG), 1, &params.env.fog[0]);
if (sshader.iflags & SIF_LIGHTING_DATA)
{
glUniform3fv(shader.U(gfx::SU_AMBIENT_LIGHT), 1, &params.env.ambient_light[0]);
glUniform3fv(shader.U(gfx::SU_SUN_COLOR), 1, &params.env.sun_color[0]);
glUniform3fv(shader.U(gfx::SU_SUN_DIRECTION), 1, &params.env.sun_direction[0]);
// glUniform4fv(shader.U(gfx::SU_FOG), 1, &params.env.fog[0]);
}
mshader.global_setup = true;
sshader.global_setup = true;
}
void gfx::Renderer::InvalidateSurfaceShader(SurfaceShader& sshader)
{
sshader.global_setup = false;
sshader.color = glm::vec4(-1.0f); // invalidate color
}
void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawListParams& params)
{
// determine render flags
for (auto& cmd : list)
{
if (cmd.surface->sflags & SF_BLEND)
cmd.rflags |= SRF_BLEND;
if (cmd.surface->texture)
cmd.rflags |= SRF_TEXTURE;
if ((cmd.surface->mflags & MF_SKELETAL) && cmd.skinning)
cmd.rflags |= SRF_SKELETAL;
if ((cmd.surface->sflags & SF_DEFORM_GRID) && cmd.surface->deform_tex)
cmd.rflags |= SRF_DEFORM;
if ((cmd.surface->sflags & SF_UNLIT) == 0)
cmd.rflags |= SRF_LIT;
if (cmd.color)
{
if (cmd.surface->sflags & SF_OBJECT_COLOR_MULT)
cmd.rflags |= SRF_OBJECT_COLOR;
else if (cmd.surface->sflags & SF_OBJECT_COLOR)
cmd.rflags |= SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND;
}
if (cmd.surface->sflags & SF_2SIDED)
cmd.rflags |= SRF_2SIDED;
if (cmd.surface->sflags & SF_BLEND_ADDITIVE)
cmd.rflags |= SRF_BLEND_ADDITIVE;
if ((cmd.surface->sflags & (SF_BLEND | SF_OBJECT_COLOR)) == 0)
cmd.rflags |= SRF_CULL_ALPHA;
}
// sort the list to minimize state changes
std::ranges::sort(list, [](const DrawSurfaceCmd& a, const DrawSurfaceCmd& b) {
const Surface* sa = a.surface;
const Surface* sb = b.surface;
const bool blend_a = sa->sflags & SF_BLEND;
const bool blend_b = sb->sflags & SF_BLEND;
const bool blend_a = a.rflags & SRF_BLEND;
const bool blend_b = b.rflags & SRF_BLEND;
if (blend_a != blend_b)
return blend_b; // opaque first
@ -144,9 +195,15 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
return a.dist > b.dist; // do not optimize blended, sort by distance instead
}
if (sa == sb)
if (a.surface == b.surface)
return false;
if (auto cmp = a.rflags <=> b.rflags; cmp != 0)
return cmp < 0;
const auto sa = a.surface;
const auto sb = a.surface;
if (auto cmp = sa->texture <=> sb->texture; cmp != 0)
return cmp < 0;
@ -162,10 +219,12 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
glActiveTexture(GL_TEXTURE0); // for all future bindings
// cache to eliminate fake state changes
SurfaceShader* sshader = nullptr;
const gfx::Texture* last_texture = nullptr;
const gfx::VertexArray* last_vao = nullptr;
const gfx::UniformBuffer<glm::mat4>* last_skin = nullptr;
const DeformTexture* last_deform = nullptr;
InvalidateShaders();
// enable depth test
@ -175,80 +234,76 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
// reset face culling
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
bool last_twosided = false;
// reset blending
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
bool last_blend = false;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // set to opacity blending default
bool last_blend_additive = false;
SurfaceRenderFlags last_rflags = 0;
for (const DrawSurfaceCmd& cmd : list)
{
const Surface* surface = cmd.surface;
// mesh flags
const bool skeletal_flag = surface->mflags & MF_SKELETAL;
// surface flags
const bool twosided_flag = surface->sflags & SF_2SIDED;
const bool blend_flag = surface->sflags & SF_BLEND;
const bool object_color_flag = surface->sflags & SF_OBJECT_COLOR;
const bool object_color_mult_flag = surface->sflags & SF_OBJECT_COLOR_MULT;
const bool deform_flag = surface->sflags & SF_DEFORM_GRID;
const bool unlit_flag = surface->sflags & SF_UNLIT;
// // mesh flags
// const bool skeletal_flag = surface->mflags & MF_SKELETAL;
// // surface flags
//const bool twosided_flag = cmd.rflags & SRF_2SIDED;
// const bool blend_flag = surface->sflags & SF_BLEND;
// const bool object_color_flag = surface->sflags & SF_OBJECT_COLOR;
// const bool object_color_mult_flag = surface->sflags & SF_OBJECT_COLOR_MULT;
// const bool deform_flag = surface->sflags & SF_DEFORM_GRID;
// const bool unlit_flag = surface->sflags & SF_UNLIT;
SurfaceRenderFlags rflags_diff = last_rflags ^ cmd.rflags;
// sync 2sided
if (last_twosided != twosided_flag)
if (rflags_diff & SRF_2SIDED)
{
if (twosided_flag)
if (cmd.rflags & SRF_2SIDED)
glDisable(GL_CULL_FACE);
else
glEnable(GL_CULL_FACE);
last_twosided = twosided_flag;
}
// select shader
MeshShader& mshader = skeletal_flag ? skel_mesh_shader_ : (deform_flag ? deform_mesh_shader_ : mesh_shader_);
SetupMeshShader(mshader, params);
// setup shader
SurfaceRenderFlags last_shader_rflags = last_rflags & SRF__SHADER;
SurfaceRenderFlags shader_rflags = cmd.rflags & SRF__SHADER;
if (last_shader_rflags != shader_rflags || !sshader)
{
sshader = &GetSurfaceShader(shader_rflags);
SetupSurfaceShader(*sshader, params);
}
auto shader = sshader->shader.get();
// set model matrix
if (cmd.matrices)
{
glUniformMatrix4fv(mshader.shader->U(SU_MODEL), 1, GL_FALSE, &cmd.matrices[0][0][0]);
glUniformMatrix4fv(shader->U(SU_MODEL), 1, GL_FALSE, &cmd.matrices[0][0][0]);
}
else
{ // use identity if no matrix provided
static const glm::mat4 identity(1.0f);
glUniformMatrix4fv(mshader.shader->U(SU_MODEL), 1, GL_FALSE, &identity[0][0]);
glUniformMatrix4fv(shader->U(SU_MODEL), 1, GL_FALSE, &identity[0][0]);
}
// set color
int shflags = SHF_CULL_ALPHA;
glm::vec4 color = glm::vec4(1.0f);
if (object_color_flag && cmd.color)
// sync color
if (sshader->iflags & SIF_OBJECT_COLOR)
{
// use object color and disable alpha cull
if (!object_color_mult_flag)
if (sshader->color != *cmd.color)
{
shflags &= ~SHF_CULL_ALPHA;
shflags |= SHF_BACKGROUND;
glUniform4fv(shader->U(SU_COLOR), 1, &(*cmd.color)[0]);
sshader->color = *cmd.color;
}
color = glm::vec4(*cmd.color);
}
// check unlit
if (unlit_flag)
shflags |= SHF_UNLIT;
// sync blending
if (blend_flag != last_blend)
if (rflags_diff & SRF_BLEND)
{
if (blend_flag)
if (cmd.rflags & SRF_BLEND)
{
glEnable(GL_BLEND);
glDepthMask(GL_FALSE);
@ -258,43 +313,19 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}
last_blend = blend_flag;
}
// sync blending type
if (blend_flag)
if ((cmd.rflags & SRF_BLEND) && (rflags_diff & SRF_BLEND_ADDITIVE))
{
shflags &= ~SHF_CULL_ALPHA;
const bool blend_additive = surface->sflags & SF_BLEND_ADDITIVE;
if (blend_additive != last_blend_additive)
{
if (blend_additive)
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
else
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
last_blend_additive = blend_additive;
}
// sync cull_alpha
if (mshader.flags != shflags)
{
glUniform1i(mshader.shader->U(SU_FLAGS), shflags);
mshader.flags = shflags;
}
// sync color
if (mshader.color != color)
{
glUniform4fv(mshader.shader->U(SU_COLOR), 1, &color[0]);
mshader.color = color;
if (cmd.rflags & SRF_BLEND_ADDITIVE)
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
else
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
// bind texture
if (last_texture != surface->texture.get())
if ((sshader->iflags & SIF_COLOR_TEXTURE) && last_texture != surface->texture.get())
{
GLuint tex_id = surface->texture ? surface->texture->GetId() : 0;
glActiveTexture(GL_TEXTURE0);
@ -303,14 +334,14 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
}
// bind skinning UBO
if (cmd.skinning && last_skin != cmd.skinning)
if ((sshader->iflags & SIF_SKELETAL_DATA) && last_skin != cmd.skinning)
{
glBindBufferBase(GL_UNIFORM_BUFFER, 0, cmd.skinning->GetId());
last_skin = cmd.skinning;
}
// bind deform texture
if (deform_flag && surface->deform_tex.get() != last_deform)
if ((sshader->iflags & SIF_DEFORM_DATA) && surface->deform_tex.get() != last_deform)
{
const auto& deform_tex = *surface->deform_tex;
GLuint tex_id = deform_tex.GetId();
@ -324,7 +355,7 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
deform_info_mat[0] = deform_info.min;
deform_info_mat[1] = deform_info.max;
deform_info_mat[2] = glm::vec3(deform_info.max_offset, 0.0f, 0.0f);
glUniformMatrix3fv(mshader.shader->U(SU_DEFORM_INFO), 1, GL_FALSE, &deform_info_mat[0][0]);
glUniformMatrix3fv(shader->U(SU_DEFORM_INFO), 1, GL_FALSE, &deform_info_mat[0][0]);
}
// bind VAO
@ -340,6 +371,8 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
// draw
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(num_tris * 3U), GL_UNSIGNED_INT,
(void*)(first_tri * 3U * sizeof(GLuint)));
last_rflags = cmd.rflags;
}
// reset this as it is rare and other stuff might not reset this

View File

@ -5,6 +5,7 @@
#include "draw_list.hpp"
#include "shader.hpp"
#include "surface_shader.hpp"
namespace gfx
{
@ -27,14 +28,14 @@ struct DrawListParams
size_t screen_height = 0;
};
struct MeshShader
struct SurfaceShader
{
std::unique_ptr<Shader> shader;
SurfaceShaderInputFlags iflags;
// cached state to avoid redundant uniform updates which are expensive especially on WebGL
bool global_setup = false;
glm::vec4 color = glm::vec4(-1.0f); // invalid to force initial setup
int flags = 0;
};
class Renderer
@ -47,17 +48,17 @@ private:
void SetupBeamVA();
void InvalidateShaders();
void InvalidateMeshShader(MeshShader& mshader);
void SetupMeshShader(MeshShader& mshader, const DrawListParams& params);
SurfaceShader& GetSurfaceShader(SurfaceRenderFlags flags);
void SetupSurfaceShader(SurfaceShader& sshader, const DrawListParams& params);
void InvalidateSurfaceShader(SurfaceShader& sshader);
void DrawSurfaceList(std::span<DrawSurfaceCmd> queue, const DrawListParams& params);
void DrawBeamList(std::span<DrawBeamCmd> queue, const DrawListParams& params);
void DrawHudList(std::span<DrawHudCmd> queue, const DrawListParams& params);
private:
MeshShader mesh_shader_;
MeshShader skel_mesh_shader_;
MeshShader deform_mesh_shader_;
std::map<SurfaceRenderFlags, SurfaceShader> surface_shaders_;
std::unique_ptr<Shader> solid_shader_;
std::unique_ptr<BufferObject> beam_segments_vbo_;

27
src/gfx/shader_common.hpp Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "shader_defs.hpp"
#include "client/gl.hpp"
#ifndef PG_GLES
#define GLSL_VERSION \
"#version 330 core\n" \
"\n"
#else
#define GLSL_VERSION \
"#version 300 es\n" \
"precision highp float;\n" \
"\n"
#endif
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define SHADER_DEFS \
"#define MAX_LIGHTS " STRINGIFY(SD_MAX_LIGHTS) "\n" \
"#define MAX_BONES " STRINGIFY(SD_MAX_BONES) "\n" \
"\n"
#define SHADER_HEADER \
GLSL_VERSION \
SHADER_DEFS

View File

@ -2,7 +2,3 @@
#define SD_MAX_LIGHTS 4
#define SD_MAX_BONES 128
#define SHF_CULL_ALPHA 1
#define SHF_BACKGROUND 2
#define SHF_UNLIT 4

View File

@ -1,304 +1,9 @@
#include "shader_sources.hpp"
#include "shader_defs.hpp"
#include "client/gl.hpp"
#ifndef PG_GLES
#define GLSL_VERSION \
"#version 330 core\n" \
"\n"
#else
#define GLSL_VERSION \
"#version 300 es\n" \
"precision highp float;\n" \
"\n"
#endif
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define SHADER_DEFS \
"#define MAX_LIGHTS " STRINGIFY(SD_MAX_LIGHTS) "\n" \
"#define MAX_BONES " STRINGIFY(SD_MAX_BONES) "\n" \
"#define SHF_CULL_ALPHA " STRINGIFY(SHF_CULL_ALPHA) "\n" \
"#define SHF_BACKGROUND " STRINGIFY(SHF_BACKGROUND) "\n" \
"#define SHF_UNLIT " STRINGIFY(SHF_UNLIT) "\n" \
"\n"
#define SHADER_HEADER \
GLSL_VERSION \
SHADER_DEFS
#define MESH_MATRICES_GLSL R"GLSL(
uniform mat4 u_view_proj;
uniform mat4 u_model; // Transform matrix
)GLSL"
#define LIGHT_MATRICES_GLSL R"GLSL(
uniform vec3 u_ambient_light;
uniform vec3 u_sun_direction;
uniform vec3 u_sun_color;
uniform vec4 u_fog;
uniform int u_flags;
uniform int u_num_lights;
uniform vec3 u_light_positions[MAX_LIGHTS];
uniform vec4 u_light_colors_rs[MAX_LIGHTS]; // rgb = color, a = radius
)GLSL"
#define COMPUTE_LIGHTS_GLSL R"GLSL(
// Example sun values (can later be uniforms)
//vec3 u_sun_direction = normalize(vec3(0.3, 0.5, -0.8)); // direction from which sunlight comes
//vec3 u_sun_color = vec3(1.0, 0.95, 0.7) * 0.9; // warm sunlight color
//uniform vec3 u_ambient_light;
vec3 ComputeLights(in vec3 sector_pos, in vec3 sector_normal)
{
if ((u_flags & SHF_UNLIT) > 0)
return vec3(1.0);
// Base ambient
vec3 color = u_ambient_light; //vec3(0.5, 0.5, 0.5) * 0.9; // u_ambient_light
// Sunlight contribution
float sun_dot = max(dot(sector_normal, -u_sun_direction), 0.0);
color += u_sun_color * sun_dot;
// Point lights
for (int i = 0; i < u_num_lights; ++i) {
vec3 light_pos = u_light_positions[i];
vec3 light_color = u_light_colors_rs[i].rgb;
float light_radius = u_light_colors_rs[i].a;
vec3 to_light = light_pos - sector_pos;
float dist2 = dot(to_light, to_light);
if (dist2 < light_radius * light_radius) {
float dist = sqrt(dist2);
float attenuation = 1.0 - (dist / light_radius);
float dot_term = max(dot(sector_normal, normalize(to_light)), 0.0);
color += light_color * dot_term * attenuation;
}
}
return color;
}
)GLSL"
#include "shader_common.hpp"
// Zdrojove kody shaderu
static const char* const s_srcs[] = {
// SS_MESH_VERT
SHADER_HEADER
R"GLSL(
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec3 a_normal;
layout (location = 2) in vec4 a_color;
layout (location = 3) in vec2 a_uv;
)GLSL"
MESH_MATRICES_GLSL
LIGHT_MATRICES_GLSL
COMPUTE_LIGHTS_GLSL
R"GLSL(
out vec2 v_uv;
out vec3 v_color;
void main() {
vec4 world_pos = u_model * vec4(a_pos, 1.0);
vec3 world_normal = normalize(mat3(u_model) * a_normal);
gl_Position = u_view_proj * world_pos;
v_uv = a_uv;
v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb;
}
)GLSL",
// SS_MESH_FRAG
SHADER_HEADER
R"GLSL(
in vec2 v_uv;
in vec3 v_color;
uniform sampler2D u_tex;
uniform vec4 u_color;
uniform int u_flags;
layout (location = 0) out vec4 o_color;
void main() {
o_color = vec4(texture(u_tex, v_uv));
if ((u_flags & SHF_BACKGROUND) > 0)
{
o_color = mix(u_color, o_color, o_color.a);
}
else
{
o_color *= u_color;
}
if ((u_flags & SHF_CULL_ALPHA) > 0)
{
if (o_color.a < 0.5)
discard;
}
o_color.rgb *= v_color; // Apply vertex color
//o_color = vec4(1.0, 0.0, 0.0, 1.0);
}
)GLSL",
// SS_SKEL_MESH_VERT
SHADER_HEADER
R"GLSL(
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec3 a_normal;
layout (location = 2) in vec4 a_color;
layout (location = 3) in vec2 a_uv;
layout (location = 5) in ivec4 a_bone_ids;
layout (location = 6) in vec4 a_bone_weights;
layout (std140) uniform Bones {
mat4 u_bone_matrices[MAX_BONES];
};
)GLSL"
MESH_MATRICES_GLSL
LIGHT_MATRICES_GLSL
COMPUTE_LIGHTS_GLSL
R"GLSL(
out vec2 v_uv;
out vec3 v_color;
void main() {
mat4 bone_transform = mat4(0.0);
for (int i = 0; i < 4; ++i) {
int bone_id = a_bone_ids[i];
if (bone_id >= 0) {
bone_transform += u_bone_matrices[bone_id] * a_bone_weights[i];
}
}
vec4 world_pos = bone_transform * vec4(a_pos, 1.0);
vec3 world_normal = normalize(mat3(bone_transform) * a_normal);
gl_Position = u_view_proj * world_pos;
v_uv = a_uv;
v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb;
}
)GLSL",
// SS_SKEL_MESH_FRAG
SHADER_HEADER
R"GLSL(
in vec2 v_uv;
in vec3 v_color;
uniform sampler2D u_tex;
uniform vec4 u_color;
uniform int u_flags;
layout (location = 0) out vec4 o_color;
void main() {
o_color = vec4(texture(u_tex, v_uv));
if ((u_flags & SHF_BACKGROUND) > 0)
{
o_color = mix(u_color, o_color, o_color.a);
}
else
{
o_color *= u_color;
}
if ((u_flags & SHF_CULL_ALPHA) > 0)
{
if (o_color.a < 0.5)
discard;
}
o_color.rgb *= v_color; // Apply vertex color
//o_color = vec4(1.0, 0.0, 0.0, 1.0);
}
)GLSL",
// SS_DEFORM_MESH_VERT
SHADER_HEADER
R"GLSL(
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec3 a_normal;
layout (location = 2) in vec4 a_color;
layout (location = 3) in vec2 a_uv;
)GLSL"
MESH_MATRICES_GLSL
LIGHT_MATRICES_GLSL
COMPUTE_LIGHTS_GLSL
R"GLSL(
uniform mediump sampler3D u_deform_tex;
uniform mat3 u_deform_info;
out vec2 v_uv;
out vec3 v_color;
void main() {
vec3 deform_pos = (a_pos - u_deform_info[0]) / (u_deform_info[1] - u_deform_info[0]);
vec3 pos = a_pos + texture(u_deform_tex, deform_pos).xyz * u_deform_info[2].x;
//vec3 pos = a_pos + u_deform_info[0] * u_deform_info[2].x;
//vec3 pos = a_pos + vec3(0.0, 0.0, 1.0);
vec4 world_pos = u_model * vec4(pos, 1.0);
vec3 world_normal = normalize(mat3(u_model) * a_normal);
gl_Position = u_view_proj * world_pos;
v_uv = a_uv;
v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb;
}
)GLSL",
// SS_DEFORM_MESH_FRAG
SHADER_HEADER
R"GLSL(
in vec2 v_uv;
in vec3 v_color;
uniform sampler2D u_tex;
uniform vec4 u_color;
uniform int u_flags;
layout (location = 0) out vec4 o_color;
void main() {
o_color = vec4(texture(u_tex, v_uv));
if ((u_flags & SHF_BACKGROUND) > 0)
{
o_color = mix(u_color, o_color, o_color.a);
}
else
{
o_color *= u_color;
}
if ((u_flags & SHF_CULL_ALPHA) > 0)
{
if (o_color.a < 0.5)
discard;
}
o_color.rgb *= v_color; // Apply vertex color
//o_color = vec4(1.0, 0.0, 0.0, 1.0);
}
)GLSL",
// SS_SOLID_VERT
SHADER_HEADER
R"GLSL(

View File

@ -7,15 +7,6 @@ namespace gfx
{
enum ShaderSource
{
SS_MESH_VERT,
SS_MESH_FRAG,
SS_SKEL_MESH_VERT,
SS_SKEL_MESH_FRAG,
SS_DEFORM_MESH_VERT,
SS_DEFORM_MESH_FRAG,
SS_SOLID_VERT,
SS_SOLID_FRAG,

View File

@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
namespace gfx
{
using SurfaceRenderFlags = uint16_t;
enum SurfaceRenderFlag : SurfaceRenderFlags
{
// dont require shader switch
SRF_BLEND_ADDITIVE = 1,
SRF_2SIDED = 2,
// require shader switch
SRF_CULL_ALPHA = 4,
SRF_OBJECT_COLOR = 8,
SRF_OBJECT_COLOR_BACKGROUND = 16,
SRF_LIT = 32,
SRF_DEFORM = 64,
SRF_SKELETAL = 128,
SRF_TEXTURE = 256,
SRF__SHADER = SRF_CULL_ALPHA | SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND | SRF_LIT | SRF_SKELETAL | SRF_DEFORM | SRF_TEXTURE,
// order affects visual result
SRF_BLEND = 512,
};
};

189
src/gfx/surface_shader.cpp Normal file
View File

@ -0,0 +1,189 @@
#include "surface_shader.hpp"
#include <string>
#include "shader_common.hpp"
std::unique_ptr<gfx::Shader> gfx::CreateSurfaceShader(SurfaceRenderFlags flags, SurfaceShaderInputFlags& input_flags)
{
std::string vert_attributes, vert_uniforms, vert_outs, vert_funcs, vert_pos_calc, vert_main;
std::string frag_ins, frag_uniforms, frag_funcs, frag_main;
// default unlit untextured skeleton
vert_attributes = R"GLSL(
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec3 a_normal;
layout (location = 2) in vec4 a_color;
layout (location = 3) in vec2 a_uv;
)GLSL";
vert_uniforms = R"GLSL(
uniform mat4 u_view_proj;
uniform mat4 u_model;
)GLSL";
vert_outs = R"GLSL(
out vec3 v_color;
)GLSL";
vert_pos_calc = R"GLSL(
vec4 world_pos = u_model * vec4(a_pos, 1.0);
vec3 world_normal = normalize(mat3(u_model) * a_normal);
)GLSL";
vert_main = R"GLSL(
gl_Position = u_view_proj * world_pos;
v_color = a_color.rgb;
)GLSL";
frag_ins = R"GLSL(
in vec3 v_color;
)GLSL";
frag_main = R"GLSL(
o_color = vec4(1.0);
)GLSL";
input_flags = 0;
// color texture
if (flags & SRF_TEXTURE)
{
vert_outs += "out vec2 v_uv;\n";
vert_main += "v_uv = a_uv;\n";
frag_ins += "in vec2 v_uv;\n";
frag_uniforms += "uniform sampler2D u_tex;\n";
frag_main += "o_color *= texture(u_tex, v_uv);\n";
input_flags |= SIF_COLOR_TEXTURE;
}
// any object color type
if (flags & (SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND))
{
frag_uniforms += "uniform vec4 u_color;\n";
if (flags & SRF_OBJECT_COLOR_BACKGROUND)
{
frag_main += "o_color = mix(u_color, o_color, o_color.a);\n";
}
else // just multiply
{
frag_main += "o_color *= u_color;\n";
}
input_flags |= SIF_OBJECT_COLOR;
}
// alpha culling
if (flags & SRF_CULL_ALPHA)
{
frag_main += "if (o_color.a < 0.5) discard;\n";
}
// lighting
if (flags & SRF_LIT)
{
vert_uniforms += R"GLSL(
// global
uniform vec3 u_ambient_light;
uniform vec3 u_sun_direction;
uniform vec3 u_sun_color;
// local
uniform int u_num_lights;
uniform vec3 u_light_positions[MAX_LIGHTS];
uniform vec4 u_light_colors_rs[MAX_LIGHTS]; // rgb = color, a = radius
)GLSL";
vert_funcs += R"GLSL(
vec3 ComputeLights(in vec3 sector_pos, in vec3 sector_normal)
{
// Base ambient
vec3 color = u_ambient_light;
// Sunlight contribution
float sun_dot = max(dot(sector_normal, -u_sun_direction), 0.0);
color += u_sun_color * sun_dot;
// Point lights
for (int i = 0; i < u_num_lights; ++i) {
vec3 light_pos = u_light_positions[i];
vec3 light_color = u_light_colors_rs[i].rgb;
float light_radius = u_light_colors_rs[i].a;
vec3 to_light = light_pos - sector_pos;
float dist2 = dot(to_light, to_light);
if (dist2 < light_radius * light_radius) {
float dist = sqrt(dist2);
float attenuation = 1.0 - (dist / light_radius);
float dot_term = max(dot(sector_normal, normalize(to_light)), 0.0);
color += light_color * dot_term * attenuation;
}
}
return color;
}
)GLSL";
vert_main += "v_color *= ComputeLights(world_pos.xyz, world_normal);\n";
input_flags |= SIF_LIGHTING_DATA;
}
if (flags & SRF_SKELETAL) // skeletal deform
{
vert_attributes += R"GLSL(
layout (location = 5) in ivec4 a_bone_ids;
layout (location = 6) in vec4 a_bone_weights;
)GLSL";
vert_uniforms += R"GLSL(
layout (std140) uniform Bones {
mat4 u_bone_matrices[MAX_BONES];
};
)GLSL";
vert_pos_calc = R"GLSL(
mat4 bone_transform = mat4(0.0);
for (int i = 0; i < 4; ++i) {
int bone_id = a_bone_ids[i];
if (bone_id >= 0) {
bone_transform += u_bone_matrices[bone_id] * a_bone_weights[i];
}
}
vec4 world_pos = bone_transform * vec4(a_pos, 1.0);
vec3 world_normal = normalize(mat3(bone_transform) * a_normal);
)GLSL";
input_flags |= SIF_SKELETAL_DATA;
}
else if (flags & SRF_DEFORM) // grid deform
{
vert_uniforms += R"GLSL(
uniform mediump sampler3D u_deform_tex;
uniform mat3 u_deform_info;
)GLSL";
vert_pos_calc = R"GLSL(
vec3 deform_pos = (a_pos - u_deform_info[0]) / (u_deform_info[1] - u_deform_info[0]);
vec3 pos = a_pos + texture(u_deform_tex, deform_pos).xyz * u_deform_info[2].x;
vec4 world_pos = u_model * vec4(pos, 1.0);
vec3 world_normal = normalize(mat3(u_model) * a_normal);
)GLSL";
input_flags |= SIF_DEFORM_DATA;
}
frag_main += "o_color.rgb *= v_color;";
vert_main = vert_pos_calc + vert_main;
std::string vertex_src = SHADER_HEADER + vert_attributes + vert_uniforms + vert_outs + vert_funcs + "\nvoid main() {\n" + vert_main + "\n}\n";
std::string fragment_src = SHADER_HEADER + frag_ins + frag_uniforms + "\nlayout (location = 0) out vec4 o_color;\n" + frag_funcs + "\nvoid main() {\n" + frag_main + "\n}\n";
return std::make_unique<Shader>(vertex_src.c_str(), fragment_src.c_str());
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <memory>
#include "shader.hpp"
#include "surface_render_flags.hpp"
namespace gfx
{
using SurfaceShaderInputFlags = uint8_t;
enum SurfaceShaderInputFlag : SurfaceShaderInputFlags
{
SIF_COLOR_TEXTURE = 1,
SIF_OBJECT_COLOR = 2,
SIF_LIGHTING_DATA = 4,
SIF_SKELETAL_DATA = 8,
SIF_DEFORM_DATA = 16,
};
std::unique_ptr<Shader> CreateSurfaceShader(SurfaceRenderFlags flags, SurfaceShaderInputFlags& input_flags);
}