From 4394179a71c24e49f393bc535cfb85376ec91d60 Mon Sep 17 00:00:00 2001 From: samstalhandske Date: Tue, 3 Feb 2026 20:31:12 +0100 Subject: [PATCH] Uniform colors --- color.odin | 47 +++++++++++++++++ material.odin | 8 ++- pass.odin | 6 +++ renderer.odin | 77 ++++++++++++++++++++++------ renderer_backend_opengl_windows.odin | 55 ++++++++++---------- shader.odin | 44 ++++++++++++++++ 6 files changed, 189 insertions(+), 48 deletions(-) create mode 100644 color.odin diff --git a/color.odin b/color.odin new file mode 100644 index 0000000..7eb782f --- /dev/null +++ b/color.odin @@ -0,0 +1,47 @@ +package renderer + +import "core:math/rand" +Color :: union { + RGB_Color, + RGBA_Color, +} + +RGB_Color :: [3]u8 +RGBA_Color :: [4]u8 + +color_to_f32 :: proc { + rgb_color_to_f32_array, + rgba_color_to_f32_array, +} + +rgb_color_to_f32_array :: proc(color: RGB_Color) -> [3]f32 { + return { + f32(color.r) / f32(max(u8)), + f32(color.g) / f32(max(u8)), + f32(color.b) / f32(max(u8)), + } +} + +rgba_color_to_f32_array :: proc(color: RGBA_Color) -> [4]f32 { + return { + f32(color.r) / f32(max(u8)), + f32(color.g) / f32(max(u8)), + f32(color.b) / f32(max(u8)), + f32(color.a) / f32(max(u8)), + } +} + +get_random_color_rgb :: proc() -> RGB_Color { + r := u8(rand.uint32() / u32(max(u8))) + g := u8(rand.uint32() / u32(max(u8))) + b := u8(rand.uint32() / u32(max(u8))) + + return { r, g, b } +} + +get_random_color_rgba :: proc() -> RGBA_Color { + c := get_random_color_rgb() + a := u8(rand.uint32() / u32(max(u8))) + + return { c.r, c.g, c.b, a } +} \ No newline at end of file diff --git a/material.odin b/material.odin index 6cd1029..28f142a 100644 --- a/material.odin +++ b/material.odin @@ -12,10 +12,12 @@ Material :: struct { textures: [MATERIAL_MAX_TEXTURES]^Texture, texture_count: u8, + uniforms: []Uniform, + uv_scale: [2]f32, // TODO: SS - Move? } -create_material :: proc(program: ^Shader_Program, textures: []^Texture) -> (Material, bool) { +create_material :: proc(program: ^Shader_Program, textures: []^Texture, uniforms: []Uniform) -> (Material, bool) { m: Material m.shader_program = program @@ -39,7 +41,9 @@ create_material :: proc(program: ^Shader_Program, textures: []^Texture) -> (Mate // TODO: SS - Should we return false here? } - m.uv_scale = { 2.0, 2.0 } // NOTE: SS - Hardcoded. + + m.uniforms = uniforms + m.uv_scale = { 1.0, 1.0 } // NOTE: SS - Hardcoded. return m, true } \ No newline at end of file diff --git a/pass.odin b/pass.odin index c12aa67..60f3014 100644 --- a/pass.odin +++ b/pass.odin @@ -34,6 +34,7 @@ Uniform :: union { Uniform_Float, Uniform_Matrix4f32, Uniform_Vector3, + Uniform_Color, } Uniform_Texture :: struct { @@ -56,6 +57,11 @@ Uniform_Vector3 :: struct { value: ^[3]f32, } +Uniform_Color :: struct { + name: string, + value: ^Color, +} + Post_Processing_Node :: struct { uniforms: []Uniform, output: ^Render_Target, diff --git a/renderer.odin b/renderer.odin index baca164..0b0f8e3 100644 --- a/renderer.odin +++ b/renderer.odin @@ -37,14 +37,6 @@ get_aspect_ratio :: proc(renderer: ^Renderer) -> f32 { return f32(viewport.width) / f32(viewport.height) } -Color :: union { - RGB_Color, - RGBA_Color, -} - -RGB_Color :: [3]u8 -RGBA_Color :: [4]u8 - create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) { renderer := new(Renderer) renderer.surface_ptr = surface_ptr @@ -153,14 +145,14 @@ render_frame :: proc(renderer: ^Renderer, texture_to_present: ^Texture, clear_co return } - view_matrix, _ := get_camera_view_matrix(renderer.active_camera) - projection_matrix, _ := get_camera_projection_matrix(renderer, renderer.active_camera) + camera_view_matrix, _ := get_camera_view_matrix(renderer.active_camera) + camera_projection_matrix, _ := get_camera_projection_matrix(renderer, renderer.active_camera) set_clear_color(renderer, clear_color) clear_screen(renderer, true, true) for i in 0 ..< renderer.pipeline.amount_of_passes { - execute_pass(renderer, renderer.pipeline.passes[i], view_matrix, projection_matrix) + execute_pass(renderer, renderer.pipeline.passes[i], camera_view_matrix, camera_projection_matrix) } if texture_to_present != nil { // Present. @@ -243,7 +235,7 @@ execute_pass :: proc(renderer: ^Renderer, pass: ^Pass, view_matrix, projection_m scale := linalg.matrix4_scale(dc.scale) model_matrix *= translation * rotation * scale - + activate_material(&dc.material, model_matrix, view_matrix, projection_matrix) draw_mesh(&dc.mesh) @@ -295,11 +287,9 @@ execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing continue } - if set_shader_uniform(node.program, t) { - mat.textures[mat.texture_count] = t.value - mat.texture_count += 1 - } - else { + mat.textures[mat.texture_count] = t.value + mat.texture_count += 1 + if !set_shader_uniform(node.program, t) { fmt.printfln("Failed to set uniform-texture %v in program (vs: '%s', fs: '%s').", t.index, vs_path, fs_path) } } @@ -318,6 +308,11 @@ execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing fmt.printfln("Failed to set uniform-vector3 '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) } } + case Uniform_Color: { + if !set_shader_uniform(node.program, t) { + fmt.printfln("Failed to set uniform-color '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) + } + } } } @@ -334,9 +329,52 @@ destroy :: proc(renderer: ^Renderer) { free(renderer) } +@(private) activate_shader_program :: proc(program: ^Shader_Program) { + when RENDER_BACKEND_OPENGL { + opengl_activate_shader_program(program) + } +} + @(private) activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32, uv_scale: [2]f32 = { 1.0, 1.0 }) { assert(material != nil) + p := material.shader_program + + activate_shader_program(p) + + fs_path := p.fragment_shader != nil ? p.fragment_shader.path : "nil" + vs_path := p.vertex_shader != nil ? p.vertex_shader.path : "nil" + + for u in material.uniforms { + switch &t in u { + case Uniform_Texture: { + if !set_shader_uniform(p, t) { + fmt.printfln("Failed to set uniform-texture %v in program (vs: '%s', fs: '%s').", t.index, vs_path, fs_path) + } + } + case Uniform_Float: { + if !set_shader_uniform(p, t) { + fmt.printfln("Failed to set uniform-float '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) + } + } + case Uniform_Matrix4f32: { + if !set_shader_uniform(p, t) { + fmt.printfln("Failed to set uniform-matrix4f32 '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) + } + } + case Uniform_Vector3: { + if !set_shader_uniform(p, t) { + fmt.printfln("Failed to set uniform-vector3 '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) + } + } + case Uniform_Color: { + if !set_shader_uniform(p, t) { + fmt.printfln("Failed to set uniform-color '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) + } + } + } + } + when RENDER_BACKEND_OPENGL { opengl_activate_material(material, model_matrix, view_matrix, projection_matrix) } @@ -424,6 +462,11 @@ distance_to_camera :: proc(camera: ^Camera, position: [3]f32) -> f32 { @(private) activate_fullscreen_material :: proc(renderer: ^Renderer, material: ^Material) { // TODO: SS - Maybe remove. assert(renderer != nil) + assert(material != nil) + assert(material.shader_program != nil) + + activate_shader_program(material.shader_program) + when RENDER_BACKEND_OPENGL { opengl_activate_fullscreen_material(material) } diff --git a/renderer_backend_opengl_windows.odin b/renderer_backend_opengl_windows.odin index 516aaf6..5663e80 100644 --- a/renderer_backend_opengl_windows.odin +++ b/renderer_backend_opengl_windows.odin @@ -110,19 +110,14 @@ when RENDER_BACKEND_OPENGL { } opengl_set_clear_color :: proc(renderer: ^Renderer, color: Color) { - r, g, b, a: u8 = max(u8), max(u8), max(u8), max(u8) - + r, g, b, a: f32 = 1.0, 1.0, 1.0, 1.0 + switch &c in color { - case RGB_Color: r, g, b = expand_values(c.rgb) - case RGBA_Color: r, g, b, a = expand_values(c.rgba) + case RGB_Color: r, g, b = expand_values(color_to_f32(c)) + case RGBA_Color: r, g, b, a = expand_values(color_to_f32(c)) } - gl.ClearColor( - f32(r) / f32(max(u8)), - f32(g) / f32(max(u8)), - f32(b) / f32(max(u8)), - f32(a) / f32(max(u8)), - ) + gl.ClearColor(r, g, b, a) } opengl_clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) { @@ -290,11 +285,15 @@ when RENDER_BACKEND_OPENGL { } } + opengl_activate_shader_program :: proc(program: ^Shader_Program) { + gl.UseProgram(program.backend.handle) + } + opengl_activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) { - gl.UseProgram(material.shader_program.backend.handle) - opengl_activate_bind_textures_in_material(material) + // TODO: SS - Move uniforms up one step to the renderer-agnostic part of the codebase. + model_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_model_matrix") if model_matrix_loc >= 0 { model_matrix_as_f32_array := transmute([16]f32)(model_matrix) @@ -321,17 +320,8 @@ when RENDER_BACKEND_OPENGL { opengl_activate_fullscreen_material :: proc(material: ^Material) { // TODO: SS - Maybe remove. assert(material != nil) - gl.UseProgram(material.shader_program.backend.handle) - assert(material.texture_count > 0) - - gl.ActiveTexture(gl.TEXTURE0) - gl.BindTexture(gl.TEXTURE_2D, material.textures[0].backend.handle) - - loc := gl.GetUniformLocation(material.shader_program.backend.handle, "texture0") - if loc != -1 { - gl.Uniform1i(loc, 0) - } + opengl_activate_bind_textures_in_material(material) gl.Disable(gl.DEPTH_TEST) gl.DepthMask(gl.FALSE) @@ -538,13 +528,20 @@ when RENDER_BACKEND_OPENGL { status := gl.CheckFramebufferStatus(gl.FRAMEBUFFER) assert(status == gl.FRAMEBUFFER_COMPLETE) - assert(len(rt.color_textures) > 0) + texture: ^Texture = nil + if len(rt.color_textures) > 0 { + texture = rt.color_textures[0] + } + else { + texture = rt.depth_texture + } + assert(texture != nil) gl.Viewport( 0, 0, - i32(rt.color_textures[0].width), - i32(rt.color_textures[0].height), + i32(texture.width), + i32(texture.height), ) } @@ -599,7 +596,7 @@ when RENDER_BACKEND_OPENGL { opengl_set_shader_uniform_texture :: proc(program: ^Shader_Program, uniform: Uniform_Texture) -> bool { - gl.UseProgram(program.backend.handle) + opengl_activate_shader_program(program) i := uniform.index @@ -617,7 +614,7 @@ when RENDER_BACKEND_OPENGL { } opengl_set_shader_uniform_float :: proc(program: ^Shader_Program, uniform: Uniform_Float) -> bool { - gl.UseProgram(program.backend.handle) + opengl_activate_shader_program(program) loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name)) if loc < 0 { @@ -632,7 +629,7 @@ when RENDER_BACKEND_OPENGL { // TODO: SS - This procedure and _float ^^ are very similar and will do pretty much the same thing but with calls to different gl.Uniform-- procedures. Make it generic instead then switch on the type? opengl_set_shader_uniform_matrix4f32 :: proc(program: ^Shader_Program, uniform: Uniform_Matrix4f32) -> bool { - gl.UseProgram(program.backend.handle) + opengl_activate_shader_program(program) loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name)) if loc < 0 { @@ -647,7 +644,7 @@ when RENDER_BACKEND_OPENGL { } opengl_set_shader_uniform_vector3 :: proc(program: ^Shader_Program, uniform: Uniform_Vector3) -> bool { - gl.UseProgram(program.backend.handle) + opengl_activate_shader_program(program) loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name)) if loc < 0 { diff --git a/shader.odin b/shader.odin index 44b8e34..182b74c 100644 --- a/shader.odin +++ b/shader.odin @@ -128,6 +128,7 @@ set_shader_uniform :: proc { // TODO: SS - Improve setting shader uniforms. A bi set_shader_uniform_float, set_shader_uniform_matrix4f32, set_shader_uniform_vector3, + set_shader_uniform_color, } set_shader_uniform_texture :: proc(program: ^Shader_Program, uniform: Uniform_Texture) -> bool { @@ -190,5 +191,48 @@ set_shader_uniform_vector3 :: proc(program: ^Shader_Program, uniform: Uniform_Ve return opengl_set_shader_uniform_vector3(program, uniform) } + return false +} + +set_shader_uniform_color :: proc(program: ^Shader_Program, uniform: Uniform_Color) -> bool { + assert(program != nil) + assert(len(uniform.name) > 0) + + if uniform.value == nil { + return false + } + + // TODO: SS - Somehow verify that 'uniform.name' exists in the shader(s)? + + switch &c in uniform.value { + case RGB_Color: { + rgb := color_to_f32(c) + + u := Uniform_Vector3 { + name = uniform.name, + value = &rgb, + } + + when RENDER_BACKEND_OPENGL { + return opengl_set_shader_uniform_vector3(program, u) + } + } + case RGBA_Color: { + rgba := color_to_f32(c) + + // u := Uniform_Vector4 { + // name = uniform.name, + // value = &rgba, + // } + + // TODO: SS - Set vector4. + // when RENDER_BACKEND_OPENGL { + // return opengl_set_shader_uniform_vector4(program, uniform) + // } + assert(false, "TODO: SS - Allow setting rgba in uniform.") + } + } + + return false } \ No newline at end of file