Kaynağa Gözat

Premultiplied alpha (D3D implemented)

Karl Zylinski 3 ay önce
ebeveyn
işleme
3089ea0c20

+ 5 - 0
.sublime/karl2d.sublime-project

@@ -36,6 +36,11 @@
 					"shell_cmd": "odin run . -vet -strict-style -keep-executable",
 					"working_dir": "$project_path/../examples/box2d"
 				},
+				{
+					"name": "premultiplied_alpha",
+					"shell_cmd": "odin run . -vet -strict-style -keep-executable -debug -define:KARL2D_RENDER_BACKEND=d3d11",
+					"working_dir": "$project_path/../examples/premultiplied_alpha"
+				},
 				{
 					"name": "raylib_ports/2d_camera",
 					"shell_cmd": "odin run . -vet -strict-style -keep-executable -debug",

BIN
examples/premultiplied_alpha/plop.png


+ 39 - 0
examples/premultiplied_alpha/premultiplied_alpha.odin

@@ -0,0 +1,39 @@
+package karl2d_example_premultiplied_alpha
+
+import k2 "../.."
+import "core:mem"
+import "core:log"
+import "core:fmt"
+
+_ :: fmt
+_ :: mem
+
+main :: proc() {
+	context.logger = log.create_console_logger()
+
+	k2.init(1080, 1080, "Karl2D Premultiplied Alpha")
+	k2.set_window_position(300, 100)
+
+	// Load a texture and premultiply the alpha while loading it.
+	// Note: In a real game you might precompute this, the premultiply-on-load will slow the load
+	// down. However, you can start with doing it on load and precompute it later.
+	tex := k2.load_texture_from_file("plop.png", options = { .Premultiply_Alpha })
+	
+	// Set the rendering to use premultiplied alpha when blending.
+	k2.set_blend_mode(.Premultiplied_Alpha)
+
+	for !k2.shutdown_wanted() {
+		k2.process_events()
+		k2.clear(k2.BLUE)
+
+		src := k2.get_texture_rect(tex)
+		dst := k2.Rect { 20, 100, src.w*20, src.h*20}
+		k2.draw_texture_ex(tex, src, dst, {}, 0)
+
+		k2.present()
+		free_all(context.temp_allocator)
+	}
+
+	k2.destroy_texture(tex)
+	k2.shutdown()
+}

+ 32 - 3
karl2d.odin

@@ -308,7 +308,7 @@ draw_current_batch :: proc() {
 		shader.texture_bindpoints[def_tex_idx] = s.batch_texture
 	}
 
-	rb.draw(shader, s.batch_render_target, shader.texture_bindpoints, s.batch_scissor, s.vertex_buffer_cpu[:s.vertex_buffer_cpu_used])
+	rb.draw(shader, s.batch_render_target, shader.texture_bindpoints, s.batch_scissor, s.batch_blend_mode, s.vertex_buffer_cpu[:s.vertex_buffer_cpu_used])
 	s.vertex_buffer_cpu_used = 0
 }
 
@@ -757,6 +757,15 @@ draw_text_ex :: proc(font: Font_Handle, text: string, pos: Vec2, font_size: f32,
 	}
 }
 
+set_blend_mode :: proc(mode: Blend_Mode) {
+	if s.batch_blend_mode == mode {
+		return
+	}
+
+	draw_current_batch()
+	s.batch_blend_mode = mode
+}
+
 //--------------------//
 // TEXTURE MANAGEMENT //
 //--------------------//
@@ -771,8 +780,16 @@ create_texture :: proc(width: int, height: int, format: Pixel_Format) -> Texture
 	}
 }
 
-load_texture_from_file :: proc(filename: string) -> Texture {
-	img, img_err := image.load_from_file(filename, options = {.alpha_add_if_missing}, allocator = s.frame_allocator)
+load_texture_from_file :: proc(filename: string, options: Load_Texture_Options = {}) -> Texture {
+	load_options := image.Options {
+		.alpha_add_if_missing,
+	}
+
+	if .Premultiply_Alpha in options {
+		load_options += { .alpha_premultiply }
+	}
+
+	img, img_err := image.load_from_file(filename, options = load_options, allocator = s.frame_allocator)
 
 	if img_err != nil {
 		log.errorf("Error loading texture %v: %v", filename, img_err)
@@ -1262,6 +1279,17 @@ Texture :: struct {
 	height: int,
 }
 
+Load_Texture_Option :: enum {
+	Premultiply_Alpha,
+}
+
+Load_Texture_Options :: bit_set[Load_Texture_Option]
+
+Blend_Mode :: enum {
+	Alpha,
+	Premultiplied_Alpha, // Needs the alpha-channel to be multiplie into any texture's color channels.
+}
+
 Render_Texture :: struct {
 	texture: Texture,
 	render_target: Render_Target_Handle,
@@ -1424,6 +1452,7 @@ State :: struct {
 	batch_scissor: Maybe(Rect),
 	batch_texture: Texture_Handle,
 	batch_render_target: Render_Target_Handle,
+	batch_blend_mode: Blend_Mode,
 
 	view_matrix: Mat4,
 	proj_matrix: Mat4,

+ 31 - 5
render_backend_d3d11.odin

@@ -129,7 +129,7 @@ d3d11_init :: proc(state: rawptr, window_handle: Window_Handle, swapchain_width,
 	}
 	ch(s.device->CreateBuffer(&vertex_buffer_desc, nil, &s.vertex_buffer_gpu))
 	
-	blend_desc := d3d11.BLEND_DESC {
+	blend_alpha_desc := d3d11.BLEND_DESC {
 		RenderTarget = {
 			0 = {
 				BlendEnable = true,
@@ -144,7 +144,24 @@ d3d11_init :: proc(state: rawptr, window_handle: Window_Handle, swapchain_width,
 		},
 	}
 
-	ch(s.device->CreateBlendState(&blend_desc, &s.blend_state))
+	ch(s.device->CreateBlendState(&blend_alpha_desc, &s.blend_state_alpha))
+
+	blend_premultiplied_alpha_desc := d3d11.BLEND_DESC {
+		RenderTarget = {
+			0 = {
+				BlendEnable = true,
+				SrcBlend = .ONE,
+				DestBlend = .INV_SRC_ALPHA,
+				BlendOp = .ADD,
+				SrcBlendAlpha = .ONE,
+				DestBlendAlpha = .INV_SRC_ALPHA,
+				BlendOpAlpha = .ADD,
+				RenderTargetWriteMask = u8(d3d11.COLOR_WRITE_ENABLE_ALL),
+			},
+		},
+	}
+
+	ch(s.device->CreateBlendState(&blend_premultiplied_alpha_desc, &s.blend_state_premultiplied_alpha))
 }
 
 d3d11_shutdown :: proc() {
@@ -157,7 +174,8 @@ d3d11_shutdown :: proc() {
 	s.depth_stencil_state->Release()
 	s.rasterizer_state->Release()
 	s.swapchain->Release()
-	s.blend_state->Release()
+	s.blend_state_alpha->Release()
+	s.blend_state_premultiplied_alpha->Release()
 	s.dxgi_adapter->Release()
 
 	when ODIN_DEBUG {
@@ -196,6 +214,7 @@ d3d11_draw :: proc(
 	render_target: Render_Target_Handle,
 	bound_textures: []Texture_Handle,
 	scissor: Maybe(Rect), 
+	blend_mode: Blend_Mode,
 	vertex_buffer: []u8,
 ) {
 	if len(vertex_buffer) == 0 {
@@ -321,7 +340,13 @@ d3d11_draw :: proc(
 	}
 
 	dc->OMSetDepthStencilState(s.depth_stencil_state, 0)
-	dc->OMSetBlendState(s.blend_state, nil, ~u32(0))
+
+	switch blend_mode {
+	case .Alpha:
+		dc->OMSetBlendState(s.blend_state_alpha, nil, ~u32(0))
+	case .Premultiplied_Alpha:
+		dc->OMSetBlendState(s.blend_state_premultiplied_alpha, nil, ~u32(0))
+	}
 	dc->Draw(u32(len(vertex_buffer)/shd.vertex_size), 0)
 	dc->OMSetRenderTargets(0, nil, nil)
 	log_messages()
@@ -934,7 +959,8 @@ D3D11_State :: struct {
 	device: ^d3d11.IDevice,
 	depth_buffer: ^d3d11.ITexture2D,
 	framebuffer: ^d3d11.ITexture2D,
-	blend_state: ^d3d11.IBlendState,
+	blend_state_alpha: ^d3d11.IBlendState,
+	blend_state_premultiplied_alpha: ^d3d11.IBlendState,
 
 	textures: hm.Handle_Map(D3D11_Texture, Texture_Handle, 1024*10),
 	render_targets: hm.Handle_Map(D3D11_Render_Target, Render_Target_Handle, 1024*10),

+ 1 - 0
render_backend_gl.odin

@@ -155,6 +155,7 @@ gl_draw :: proc(
 	render_texture: Render_Target_Handle,
 	bound_textures: []Texture_Handle,
 	scissor: Maybe(Rect),
+	blend_mode: Blend_Mode,
 	vertex_buffer: []u8,
 ) {
 	gl_shd := hm.get(&s.shaders, shd.handle)

+ 1 - 0
render_backend_interface.odin

@@ -27,6 +27,7 @@ Render_Backend_Interface :: struct #all_or_none {
 		render_target: Render_Target_Handle,
 		bound_textures: []Texture_Handle,
 		scissor: Maybe(Rect),
+		blend: Blend_Mode,
 		vertex_buffer: []u8,
 	),