From 42b5f1997cdd5e99645e24dca6cb89cc7b081a09 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 1 Nov 2025 18:23:08 -0700 Subject: Add support for alpha mode --- include/gfx/core.h | 24 +++++---- include/gfx/render/llr.h | 11 +++- include/gfx/render/renderer.h | 13 +++-- include/gfx/scene.h | 1 + shaders/cook_torrance.frag | 89 +++++++++++++++++-------------- shaders/cook_torrance.vert | 24 ++++----- src/asset/model.c | 75 ++++++++++++++++---------- src/core/shader_program.c | 119 +++++++++++++++++++++++++++--------------- src/render/llr.c | 36 ++++++++++--- src/render/llr_impl.h | 6 +-- src/render/renderer.c | 46 +++++++++++++--- 11 files changed, 294 insertions(+), 150 deletions(-) diff --git a/include/gfx/core.h b/include/gfx/core.h index dc0b015..5a05cda 100644 --- a/include/gfx/core.h +++ b/include/gfx/core.h @@ -183,11 +183,12 @@ typedef struct ShaderProgramDesc { /// Shader uniform type. typedef enum { + UniformInt, UniformFloat, UniformMat4, - UniformTexture, UniformVec3, UniformVec4, + UniformTexture, UniformMat4Array } UniformType; @@ -200,10 +201,11 @@ typedef struct ShaderUniform { UniformType type; union { const Texture* texture; - mat4 mat4; - vec3 vec3; - vec4 vec4; - float scalar; + int uniform_int; + float uniform_float; + mat4 uniform_mat4; + vec3 uniform_vec3; + vec4 uniform_vec4; struct { size_t count; union { @@ -479,9 +481,13 @@ void gfx_deactivate_shader_program(const ShaderProgram*); /// gfx_activate_shader_program(). void gfx_apply_uniforms(const ShaderProgram*); -/// Set the texture uniform. +/// Set the int uniform. /// Has no effect if the shader does not contain the given uniform. -void gfx_set_texture_uniform(ShaderProgram*, const char* name, const Texture*); +void gfx_set_int_uniform(ShaderProgram*, const char* name, int value); + +/// Set the float uniform. +/// Has no effect if the shader does not contain the given uniform. +void gfx_set_float_uniform(ShaderProgram*, const char* name, float value); /// Set the matrix uniform. /// Has no effect if the shader does not contain the given uniform. @@ -495,9 +501,9 @@ void gfx_set_vec3_uniform(ShaderProgram*, const char* name, vec3); /// Has no effect if the shader does not contain the given uniform. void gfx_set_vec4_uniform(ShaderProgram*, const char* name, vec4); -/// Set the float uniform. +/// Set the texture uniform. /// Has no effect if the shader does not contain the given uniform. -void gfx_set_float_uniform(ShaderProgram*, const char* name, float value); +void gfx_set_texture_uniform(ShaderProgram*, const char* name, const Texture*); /// Set the matrix array uniform. /// Has no effect if the shader does not contain the given uniform. diff --git a/include/gfx/render/llr.h b/include/gfx/render/llr.h index 785f9cd..b30b11e 100644 --- a/include/gfx/render/llr.h +++ b/include/gfx/render/llr.h @@ -18,8 +18,10 @@ typedef struct Texture Texture; typedef struct LLR LLR; +// TODO: Move data structures to scene.h? // ----------------------------------------------------------------------------- // Data structures. +// ----------------------------------------------------------------------------- /// Light type. typedef enum LightType { EnvironmentLightType } LightType; @@ -37,6 +39,9 @@ typedef struct LightDesc { } light; } LightDesc; +/// Alpha mode. +typedef enum AlphaMode { Opaque = 0, Mask = 1, Blend = 2 } AlphaMode; + /// Describes a material. /// /// TODO: It doesn't hold the shader program anymore...It's in the Mesh. @@ -44,8 +49,10 @@ typedef struct LightDesc { /// variables. Two materials can share the same shader, but shader parameters /// generally give two materials a different appearance. typedef struct MaterialDesc { - ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; + AlphaMode alpha_mode; + float alpha_cutoff; int num_uniforms; + ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; } MaterialDesc; /// Describes a mesh. @@ -85,6 +92,7 @@ void gfx_destroy_mesh(Mesh**); // ----------------------------------------------------------------------------- // Low-level rendering. +// ----------------------------------------------------------------------------- /// Set the shader to be used for subsequent draw calls. /// The shader is not yet activated at this point. @@ -130,6 +138,7 @@ void gfx_llr_render_mesh(LLR*, const Mesh*); // ----------------------------------------------------------------------------- // Matrix stack manipulation. +// ----------------------------------------------------------------------------- /// Load an identity model matrix. Clears the matrix stack. void gfx_llr_load_identity(LLR* renderer); diff --git a/include/gfx/render/renderer.h b/include/gfx/render/renderer.h index 6cebe50..9f3231b 100644 --- a/include/gfx/render/renderer.h +++ b/include/gfx/render/renderer.h @@ -8,6 +8,7 @@ typedef struct Scene Scene; typedef struct Renderer Renderer; +// TODO: Add RenderDepth. typedef enum RenderSceneMode { RenderDefault, RenderDebug, @@ -16,10 +17,16 @@ typedef enum RenderSceneMode { RenderTangents } RenderSceneMode; +typedef enum RenderSceneFilter { + RenderOpaqueAndAlphaMasked, + RenderTransparent +} RenderSceneFilter; + typedef struct RenderSceneParams { - RenderSceneMode mode; - const Scene* scene; - const Camera* camera; + RenderSceneMode mode; + RenderSceneFilter filter; + const Scene* scene; + const Camera* camera; } RenderSceneParams; /// Render the scene. diff --git a/include/gfx/scene.h b/include/gfx/scene.h index 740a948..9747da7 100644 --- a/include/gfx/scene.h +++ b/include/gfx/scene.h @@ -82,6 +82,7 @@ const Skeleton* gfx_get_object_skeleton(const SceneObject*); /// The object's bounding box is the bounding box of its mesh geometries. aabb3 gfx_get_object_aabb(const SceneObject*); +// TODO: Remove the scene object? It only contains the root node. // ----------------------------------------------------------------------------- // Scene. // ----------------------------------------------------------------------------- diff --git a/shaders/cook_torrance.frag b/shaders/cook_torrance.frag index 9ff5a8d..c0c666c 100644 --- a/shaders/cook_torrance.frag +++ b/shaders/cook_torrance.frag @@ -10,19 +10,26 @@ uniform float MetallicFactor; uniform float RoughnessFactor; uniform vec3 EmissiveFactor; -#ifdef HAS_ALBEDO_MAP +#if HAS_TRANSPARENCY +#define ALPHA_MODE_MASK 1 +#define ALPHA_MODE_BLEND 2 +uniform int AlphaMode; +uniform float AlphaCutoff; +#endif + +#if HAS_ALBEDO_MAP uniform sampler2D BaseColorTexture; #endif -#ifdef HAS_METALLIC_ROUGHNESS_MAP +#if HAS_METALLIC_ROUGHNESS_MAP uniform sampler2D MetallicRoughnessTexture; #endif -#ifdef HAS_EMISSIVE_MAP +#if HAS_EMISSIVE_MAP uniform sampler2D EmissiveTexture; #endif -#ifdef HAS_OCCLUSION_MAP +#if HAS_OCCLUSION_MAP uniform sampler2D AmbientOcclusionTexture; #endif -#ifdef HAS_NORMAL_MAP +#if HAS_NORMAL_MAP uniform sampler2D NormalMap; #endif @@ -37,13 +44,13 @@ uniform vec3 CameraPosition; // World space. // World-space position, normal and tangent. in vec3 Position; -#ifdef HAS_NORMALS +#if HAS_NORMALS in vec3 Normal; #endif -#ifdef HAS_TANGENTS +#if HAS_TANGENTS in vec4 Tangent; #endif -#ifdef HAS_TEXCOORDS +#if HAS_TEXCOORDS in vec2 Texcoord; #endif @@ -61,7 +68,7 @@ layout (location = 0) out vec4 Colour; #if defined(HAS_NORMAL_MAP) && (defined(HAS_TANGENTS) || defined(HAS_TEXCOORDS)) vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { vec3 N = normalize(Normal); -#ifdef HAS_TANGENTS +#if HAS_TANGENTS //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N); vec3 T = Tangent.xyz; vec3 B = Tangent.w * cross(N, T); @@ -159,36 +166,19 @@ vec3 cook_torrance_IBL( void main() { - // TODO: Also use the specular F0 map from the model, and emissive. Make sure - // to use all maps. - // https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4 -#ifdef HAS_NORMAL_MAP - vec3 normalMapSample = texture(NormalMap, Texcoord).xyz * 2.0 - 1.0; - vec3 N = get_ws_normal(Normal, normalMapSample); -#elif HAS_NORMALS - vec3 N = normalize(Normal); -#endif - vec3 V = normalize(CameraPosition - Position); - vec3 R = reflect(-V, N); - // Not needed for IBL. - //vec3 L = N; - //vec3 H = normalize(L + V); - - float NdotV = max(0.0, dot(N, V)); - // Not needed for IBL. - //float NdotL = max(0.0, dot(N,L)); - //float NdotH = max(0.0, dot(N,H)); - //float HdotV = clamp(dot(H,V), 0.0, 1.0); // Clamp to prevent black spots. - - // TODO: BaseColorFactor and BaseColorTexture are vec4/rgba quantities - // respectively. Handle the alpha channel. // TODO: Other factors. -#ifdef HAS_ALBEDO_MAP - vec3 albedo = vec3(BaseColorFactor) * texture(BaseColorTexture, Texcoord).rgb; +#if HAS_ALBEDO_MAP + vec4 base_colour = vec4(BaseColorFactor) * texture(BaseColorTexture, Texcoord); #else - vec3 albedo = vec3(BaseColorFactor); + vec4 base_colour = vec4(BaseColorFactor); #endif -#ifdef HAS_METALLIC_ROUGHNESS_MAP + vec3 albedo = base_colour.rgb; +#if HAS_TRANSPARENCY + if ((AlphaMode == ALPHA_MODE_MASK) && (base_colour.a < AlphaCutoff)) { + discard; + } +#endif +#if HAS_METALLIC_ROUGHNESS_MAP // Spec: "Its green channel contains roughness values and its blue channel // contains metalness values." vec2 metal_roughness @@ -197,12 +187,12 @@ void main() #else vec2 metal_roughness = vec2(MetallicFactor, RoughnessFactor); #endif -#ifdef HAS_EMISSIVE_MAP +#if HAS_EMISSIVE_MAP vec3 emissive = EmissiveFactor * texture(EmissiveTexture, Texcoord).rgb; #else vec3 emissive = EmissiveFactor; #endif -#ifdef HAS_OCCLUSION_MAP +#if HAS_OCCLUSION_MAP float occlusion = texture(AmbientOcclusionTexture, Texcoord).r; #else float occlusion = 1.0; @@ -210,6 +200,27 @@ void main() float metallic = metal_roughness.x; float roughness = metal_roughness.y; + // TODO: Also use the specular F0 map from the model, and emissive. Make sure + // to use all maps. + // https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4 +#if HAS_NORMAL_MAP + vec3 normalMapSample = texture(NormalMap, Texcoord).xyz * 2.0 - 1.0; + vec3 N = get_ws_normal(Normal, normalMapSample); +#elif HAS_NORMALS + vec3 N = normalize(Normal); +#endif + vec3 V = normalize(CameraPosition - Position); + vec3 R = reflect(-V, N); + // Not needed for IBL. + //vec3 L = N; + //vec3 H = normalize(L + V); + + float NdotV = max(0.0, dot(N, V)); + // Not needed for IBL. + //float NdotL = max(0.0, dot(N,L)); + //float NdotH = max(0.0, dot(N,H)); + //float HdotV = clamp(dot(H,V), 0.0, 1.0); // Clamp to prevent black spots. + // For a single light direction: // vec3 brdf = cook_torrance(albedo, metallic, roughness, NdotL, NdotV, NdotH, HdotV); // vec3 Li = texture(Sky, N).rgb; @@ -268,5 +279,5 @@ void main() // //colour = B * 0.5 + 0.5; // } - Colour = vec4(colour, 1.0); + Colour = vec4(colour, base_colour.a); } diff --git a/shaders/cook_torrance.vert b/shaders/cook_torrance.vert index 5f126c0..17fe1f7 100644 --- a/shaders/cook_torrance.vert +++ b/shaders/cook_torrance.vert @@ -5,7 +5,7 @@ uniform mat4 ModelMatrix; uniform mat4 View; uniform mat4 Projection; //uniform mat4 MVP; -#ifdef HAS_JOINTS +#if HAS_JOINTS // The client should pass in an appropriate value for MAX_JOINTS. // #define MAX_JOINTS 96 // @@ -21,35 +21,35 @@ uniform mat4 JointMatrices[MAX_JOINTS]; // Use 4x4 for now to keep it simple. #endif layout (location = 0) in vec3 vPosition; -#ifdef HAS_NORMALS +#if HAS_NORMALS layout (location = 1) in vec3 vNormal; #endif -#ifdef HAS_TANGENTS +#if HAS_TANGENTS layout (location = 2) in vec4 vTangent; #endif -#ifdef HAS_TEXCOORDS +#if HAS_TEXCOORDS layout (location = 3) in vec2 vTexcoord; #endif -#ifdef HAS_JOINTS +#if HAS_JOINTS layout (location = 4) in uvec4 vJoint; layout (location = 5) in vec4 vWeight; #endif // World-space position, normal and tangent. out vec3 Position; -#ifdef HAS_NORMALS +#if HAS_NORMALS out vec3 Normal; #endif -#ifdef HAS_TANGENTS +#if HAS_TANGENTS out vec4 Tangent; #endif -#ifdef HAS_TEXCOORDS +#if HAS_TEXCOORDS out vec2 Texcoord; #endif void main() { -#ifdef HAS_JOINTS +#if HAS_JOINTS mat4 skinMatrix = vWeight.x * JointMatrices[vJoint.x] + vWeight.y * JointMatrices[vJoint.y] + @@ -59,14 +59,14 @@ void main() #else Position = vec3(ModelMatrix * vec4(vPosition, 1.0)); #endif -#ifdef HAS_NORMALS +#if HAS_NORMALS Normal = mat3(ModelMatrix) * vNormal; //Normal = normalize(ModelMatrix * vec4(vNormal, 0.0)).xyz; #endif -#ifdef HAS_TANGENTS +#if HAS_TANGENTS Tangent = vec4(mat3(ModelMatrix) * vTangent.xyz, vTangent.w); #endif -#ifdef HAS_TEXCOORDS +#if HAS_TEXCOORDS Texcoord = vTexcoord; #endif gl_Position = Projection * View * vec4(Position, 1.0); diff --git a/src/asset/model.c b/src/asset/model.c index 2ee3cd1..a97d20e 100644 --- a/src/asset/model.c +++ b/src/asset/model.c @@ -138,6 +138,7 @@ #define DEFINE_HAS_NORMAL_MAP "HAS_NORMAL_MAP" #define DEFINE_HAS_OCCLUSION_MAP "HAS_OCCLUSION_MAP" #define DEFINE_HAS_EMISSIVE_MAP "HAS_EMISSIVE_MAP" +#define DEFINE_HAS_TRANSPARENCY "HAS_TRANSPARENCY" #define DEFINE_HAS_JOINTS "HAS_JOINTS" #define DEFINE_MAX_JOINTS "MAX_JOINTS" @@ -166,6 +167,8 @@ typedef struct MeshPermutation { bool has_normal_map : 1; bool has_occlusion_map : 1; bool has_emissive_map : 1; + // Material. + bool has_transparency : 1; }; int32_t all; }; @@ -192,6 +195,7 @@ static size_t make_defines( check(has_normal_map, DEFINE_HAS_NORMAL_MAP); check(has_occlusion_map, DEFINE_HAS_OCCLUSION_MAP); check(has_emissive_map, DEFINE_HAS_EMISSIVE_MAP); + check(has_transparency, DEFINE_HAS_TRANSPARENCY); if (perm.has_joints) { defines[next].name = sstring_make(DEFINE_MAX_JOINTS); @@ -208,12 +212,12 @@ static ShaderProgram* make_shader_permutation( LOGD( "Compiling Cook-Torrance shader permutation: texcoords: %d, normals: " "%d, tangents: %d, joints: %d, weights: %d, albedo map: %d, " - "metallic-roughness map: " - "%d, normal " - "map: %d, AO map: %d, emissive map: %d", + "metallic-roughness map: %d, normal map: %d, AO map: %d, emissive map: " + "%d, has transparency: %d", perm.has_texcoords, perm.has_normals, perm.has_tangents, perm.has_joints, perm.has_weights, perm.has_albedo_map, perm.has_metallic_roughness_map, - perm.has_normal_map, perm.has_occlusion_map, perm.has_emissive_map); + perm.has_normal_map, perm.has_occlusion_map, perm.has_emissive_map, + perm.has_transparency); ShaderCompilerDefine defines[GFX_MAX_SHADER_COMPILER_DEFINES]; const size_t num_defines = make_defines(perm, defines); @@ -742,6 +746,19 @@ static bool load_texture_and_uniform( return true; } +static AlphaMode to_gfx_alpha_mode(cgltf_alpha_mode mode) { + switch (mode) { + case cgltf_alpha_mode_opaque: + return Opaque; + case cgltf_alpha_mode_mask: + return Mask; + case cgltf_alpha_mode_blend: + return Blend; + } + FAIL("unhandled alpha mode"); + return Opaque; +} + /// Load all materials from the glTF scene. /// /// Return an array of Materials such that the index of each descriptor matches @@ -770,27 +787,27 @@ static bool load_materials( assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); desc.uniforms[next_uniform++] = (ShaderUniform){ - .name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), - .type = UniformVec4, - .value.vec4 = vec4_from_array(pbr->base_color_factor)}; + .name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), + .type = UniformVec4, + .value.uniform_vec4 = vec4_from_array(pbr->base_color_factor)}; assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); desc.uniforms[next_uniform++] = - (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), - .type = UniformFloat, - .value.scalar = pbr->metallic_factor}; + (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), + .type = UniformFloat, + .value.uniform_float = pbr->metallic_factor}; assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); desc.uniforms[next_uniform++] = (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), .type = UniformFloat, - .value.scalar = pbr->roughness_factor}; + .value.uniform_float = pbr->roughness_factor}; assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); - desc.uniforms[next_uniform++] = - (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), - .type = UniformVec3, - .value.vec3 = vec3_from_array(mat->emissive_factor)}; + desc.uniforms[next_uniform++] = (ShaderUniform){ + .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), + .type = UniformVec3, + .value.uniform_vec3 = vec3_from_array(mat->emissive_factor)}; if (pbr->base_color_texture.texture) { if (!load_texture_and_uniform( @@ -837,6 +854,9 @@ static bool load_materials( assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); desc.num_uniforms = next_uniform; + desc.alpha_mode = to_gfx_alpha_mode(mat->alpha_mode); + desc.alpha_cutoff = mat->alpha_cutoff; + materials[i] = gfx_make_material(&desc); if (!materials[i]) { return false; @@ -852,27 +872,27 @@ static Material* make_default_material() { assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); desc.uniforms[desc.num_uniforms++] = - (ShaderUniform){.name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), - .type = UniformVec4, - .value.vec4 = vec4_make(1, 1, 1, 1)}; + (ShaderUniform){.name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), + .type = UniformVec4, + .value.uniform_vec4 = vec4_make(1, 1, 1, 1)}; assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); desc.uniforms[desc.num_uniforms++] = - (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), - .type = UniformFloat, - .value.scalar = 0}; + (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), + .type = UniformFloat, + .value.uniform_float = 0}; assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); desc.uniforms[desc.num_uniforms++] = - (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), - .type = UniformFloat, - .value.scalar = 1}; + (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), + .type = UniformFloat, + .value.uniform_float = 1}; assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); desc.uniforms[desc.num_uniforms++] = - (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), - .type = UniformVec3, - .value.vec3 = vec3_make(0, 0, 0)}; + (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), + .type = UniformVec3, + .value.uniform_vec3 = vec3_make(0, 0, 0)}; return gfx_make_material(&desc); } @@ -950,6 +970,7 @@ static bool load_meshes( perm.has_normal_map = mat->normal_texture.texture != 0; perm.has_occlusion_map = mat->occlusion_texture.texture != 0; perm.has_emissive_map = mat->emissive_texture.texture != 0; + perm.has_transparency = mat->alpha_mode != cgltf_alpha_mode_opaque; if (mat->has_pbr_metallic_roughness) { const cgltf_pbr_metallic_roughness* pbr = diff --git a/src/core/shader_program.c b/src/core/shader_program.c index 3cbe48d..eeb46f8 100644 --- a/src/core/shader_program.c +++ b/src/core/shader_program.c @@ -72,17 +72,23 @@ void gfx_deactivate_shader_program(const ShaderProgram* prog) { ASSERT_GL; } -static void set_texture_uniform( - GLuint prog, const char* name, int texture_unit, const Texture* texture) { +static void set_int_uniform(GLuint prog, const char* name, int value) { assert(prog != 0); assert(name); - assert(texture); const GLint location = glGetUniformLocation(prog, name); if (location >= 0) { - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(texture->target, texture->id); - glUniform1i(location, texture_unit); + glUniform1i(location, value); + } +} + +static void set_float_uniform(GLuint prog, const char* name, float value) { + assert(prog != 0); + assert(name); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniform1f(location, value); } } @@ -118,13 +124,17 @@ static void set_vec4_uniform(GLuint prog, const char* name, vec4 value) { } } -static void set_float_uniform(GLuint prog, const char* name, float value) { +static void set_texture_uniform( + GLuint prog, const char* name, int texture_unit, const Texture* texture) { assert(prog != 0); assert(name); + assert(texture); const GLint location = glGetUniformLocation(prog, name); if (location >= 0) { - glUniform1f(location, value); + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(texture->target, texture->id); + glUniform1i(location, texture_unit); } } @@ -135,23 +145,30 @@ void gfx_apply_uniforms(const ShaderProgram* prog) { for (int i = 0; i < prog->num_uniforms; ++i) { const ShaderUniform* uniform = &prog->uniforms[i]; switch (uniform->type) { - case UniformTexture: - set_texture_uniform( - prog->id, uniform->name.str, next_texture_unit, - uniform->value.texture); - next_texture_unit++; + case UniformInt: + set_int_uniform(prog->id, uniform->name.str, uniform->value.uniform_int); + break; + case UniformFloat: + set_float_uniform( + prog->id, uniform->name.str, uniform->value.uniform_float); break; case UniformMat4: - set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4, 1); + set_mat4_uniform( + prog->id, uniform->name.str, &uniform->value.uniform_mat4, 1); break; case UniformVec3: - set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3); + set_vec3_uniform( + prog->id, uniform->name.str, uniform->value.uniform_vec3); break; case UniformVec4: - set_vec4_uniform(prog->id, uniform->name.str, uniform->value.vec4); + set_vec4_uniform( + prog->id, uniform->name.str, uniform->value.uniform_vec4); break; - case UniformFloat: - set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar); + case UniformTexture: + set_texture_uniform( + prog->id, uniform->name.str, next_texture_unit, + uniform->value.texture); + next_texture_unit++; break; case UniformMat4Array: set_mat4_uniform( @@ -179,8 +196,9 @@ static ShaderUniform* get_or_allocate_uniform( // Create the uniform if it does not exist. if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) { - FAIL("Exceeded the maximum number of uniforms per shader. Please increase " - "this value."); + FAIL( + "Exceeded the maximum number of uniforms per shader. Please increase " + "this value."); return 0; } ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms]; @@ -191,21 +209,38 @@ static ShaderUniform* get_or_allocate_uniform( // The functions below save the value of a uniform in the shader program. If the // uniform does not even exist, then there is no need to store the value. -void gfx_set_texture_uniform( - ShaderProgram* prog, const char* name, const Texture* texture) { +void gfx_set_int_uniform(ShaderProgram* prog, const char* name, int value) { assert(prog); assert(name); - assert(texture); + // No need to store the uniform on our side if it does not exist in the + // program. const GLint location = glGetUniformLocation(prog->id, name); if (location < 0) { return; } ShaderUniform* uniform = get_or_allocate_uniform(prog, name); assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformTexture; - uniform->value.texture = texture; + uniform->name = sstring_make(name); + uniform->type = UniformInt; + uniform->value.uniform_int = value; +} + +void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) { + assert(prog); + assert(name); + + // No need to store the uniform on our side if it does not exist in the + // program. + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformFloat; + uniform->value.uniform_float = value; } void gfx_set_mat4_uniform( @@ -220,9 +255,9 @@ void gfx_set_mat4_uniform( } ShaderUniform* uniform = get_or_allocate_uniform(prog, name); assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformMat4; - uniform->value.mat4 = *mat; + uniform->name = sstring_make(name); + uniform->type = UniformMat4; + uniform->value.uniform_mat4 = *mat; } void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { @@ -235,9 +270,9 @@ void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { } ShaderUniform* uniform = get_or_allocate_uniform(prog, name); assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformVec3; - uniform->value.vec3 = value; + uniform->name = sstring_make(name); + uniform->type = UniformVec3; + uniform->value.uniform_vec3 = value; } void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { @@ -250,26 +285,26 @@ void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { } ShaderUniform* uniform = get_or_allocate_uniform(prog, name); assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformVec4; - uniform->value.vec4 = value; + uniform->name = sstring_make(name); + uniform->type = UniformVec4; + uniform->value.uniform_vec4 = value; } -void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) { +void gfx_set_texture_uniform( + ShaderProgram* prog, const char* name, const Texture* texture) { assert(prog); assert(name); + assert(texture); - // No need to store the uniform on our side if it does not exist in the - // program. const GLint location = glGetUniformLocation(prog->id, name); if (location < 0) { return; } ShaderUniform* uniform = get_or_allocate_uniform(prog, name); assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformFloat; - uniform->value.scalar = value; + uniform->name = sstring_make(name); + uniform->type = UniformTexture; + uniform->value.texture = texture; } void gfx_set_mat4_array_uniform( @@ -277,7 +312,7 @@ void gfx_set_mat4_array_uniform( assert(prog); assert(name); assert(mats); - + const GLint location = glGetUniformLocation(prog->id, name); if (location < 0) { return; diff --git a/src/render/llr.c b/src/render/llr.c index 76935f9..c9c6d34 100644 --- a/src/render/llr.c +++ b/src/render/llr.c @@ -54,7 +54,9 @@ static void material_make(Material* material, const MaterialDesc* desc) { assert(material); assert(desc); assert(desc->num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); - material->num_uniforms = desc->num_uniforms; + material->alpha_mode = desc->alpha_mode; + material->alpha_cutoff = desc->alpha_cutoff; + material->num_uniforms = (int8_t)desc->num_uniforms; for (int i = 0; i < desc->num_uniforms; ++i) { material->uniforms[i] = desc->uniforms[i]; } @@ -69,22 +71,27 @@ Material* gfx_make_material(const MaterialDesc* desc) { void gfx_destroy_material(Material** material) { mem_free_material(material); } +// TODO: Move this to core/shader_program. static void set_uniform(ShaderProgram* prog, const ShaderUniform* uniform) { switch (uniform->type) { - case UniformTexture: - gfx_set_texture_uniform(prog, uniform->name.str, uniform->value.texture); + case UniformInt: + gfx_set_int_uniform(prog, uniform->name.str, uniform->value.uniform_int); + break; + case UniformFloat: + gfx_set_float_uniform( + prog, uniform->name.str, uniform->value.uniform_float); break; case UniformMat4: - gfx_set_mat4_uniform(prog, uniform->name.str, &uniform->value.mat4); + gfx_set_mat4_uniform(prog, uniform->name.str, &uniform->value.uniform_mat4); break; case UniformVec3: - gfx_set_vec3_uniform(prog, uniform->name.str, uniform->value.vec3); + gfx_set_vec3_uniform(prog, uniform->name.str, uniform->value.uniform_vec3); break; case UniformVec4: - gfx_set_vec4_uniform(prog, uniform->name.str, uniform->value.vec4); + gfx_set_vec4_uniform(prog, uniform->name.str, uniform->value.uniform_vec4); break; - case UniformFloat: - gfx_set_float_uniform(prog, uniform->name.str, uniform->value.scalar); + case UniformTexture: + gfx_set_texture_uniform(prog, uniform->name.str, uniform->value.texture); break; case UniformMat4Array: gfx_set_mat4_array_uniform( @@ -104,6 +111,19 @@ static void gfx_material_activate( const ShaderUniform* uniform = &material->uniforms[i]; set_uniform(shader, uniform); } + if (material->alpha_mode != Opaque) { + set_uniform( + shader, &(ShaderUniform){.name = sstring_make("AlphaMode"), + .type = UniformInt, + .value.uniform_int = material->alpha_mode}); + } + if (material->alpha_mode == Mask) { + set_uniform( + shader, + &(ShaderUniform){.name = sstring_make("AlphaCutoff"), + .type = UniformFloat, + .value.uniform_float = material->alpha_cutoff}); + } } static void mesh_make(Mesh* mesh, const MeshDesc* desc) { diff --git a/src/render/llr_impl.h b/src/render/llr_impl.h index c85ad15..3a5455a 100644 --- a/src/render/llr_impl.h +++ b/src/render/llr_impl.h @@ -3,8 +3,6 @@ #include #include -#include "../types.h" - #include #include @@ -37,8 +35,10 @@ typedef struct Light { } Light; typedef struct Material { + AlphaMode alpha_mode; + float alpha_cutoff; + int8_t num_uniforms; ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_MATERIAL]; - int num_uniforms; } Material; typedef struct Mesh { diff --git a/src/render/renderer.c b/src/render/renderer.c index b513ed4..26b63bc 100644 --- a/src/render/renderer.c +++ b/src/render/renderer.c @@ -86,12 +86,13 @@ static ShaderProgram* load_shader(Renderer* renderer, RenderSceneMode mode) { // } typedef struct RenderState { - GfxCore* gfxcore; - LLR* llr; - Renderer* renderer; - ShaderProgram* shader; // Null to use scene shaders. - const Scene* scene; - const Anima* anima; + GfxCore* gfxcore; + LLR* llr; + Renderer* renderer; + ShaderProgram* shader; // Null to use scene shaders. + const Scene* scene; + const Anima* anima; + RenderSceneFilter filter; } RenderState; static void draw_children( @@ -153,6 +154,24 @@ static void draw_recursively( continue; } + // Filter out by material. + const Material* material = mesh->material; + if (material) { + const AlphaMode mode = material->alpha_mode; + switch (state->filter) { + case RenderOpaqueAndAlphaMasked: + if (mode == Blend) { + continue; + } + break; + case RenderTransparent: + if (mode != Blend) { + continue; + } + break; + } + } + // TODO: Here we would frustum-cull the mesh. The AABB would have to be // transformed by the model matrix. Rotation would make the AABB // relatively large, but still, the culling would be conservative. @@ -208,6 +227,20 @@ void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) { gfx_llr_set_camera(renderer->llr, camera); gfx_llr_set_aspect(renderer->llr, aspect); + // TODO: Render Opaque and Mask alpha-mode materials first, then Blend ones. + // TODO: I'm not sure if this belongs to the scene renderer per se, or if it + // is something that should be driven from the outside. Specifically, the + // caller could pass in a filter that determines what objects to render. The + // filter could include alpha mode. + // This caller would be some component that understands render passes and + // potentially renders the scene multiple times as needed. For example, a + // depth-prepass, followed by G-buffer, followed by some post-processing, + // etc. Rename this renderer to scene_renderer? + // Opaque. + state.filter = RenderOpaqueAndAlphaMasked; + draw_recursively(&state, mat4_id(), gfx_get_scene_root(scene)); + // Transparent. + state.filter = RenderTransparent; draw_recursively(&state, mat4_id(), gfx_get_scene_root(scene)); } @@ -236,6 +269,7 @@ static void update_rec(SceneNode* node, const Camera* camera, R t) { } } +// TODO: Move this outside the renderer. void gfx_update(Scene* scene, const Camera* camera, R t) { assert(scene); assert(camera); -- cgit v1.2.3