blending.asm

src/4.advanced_d3d11/3.1.blending/blending.asm · 274 lines

;---------------------------------------------------------------------------------------------------
; 3.1 Blending
;---------------------------------------------------------------------------------------------------
; Two kinds of see-through:
;
;   grass    each texel is either fully there or fully not. The pixel shader throws away (clip)
;            pixels whose alpha is low, and what is left is ordinary opaque geometry: order does
;            not matter
;   windows  partly transparent. The blend state mixes each new pixel with what is already in the
;            target: src * a + dst * (1 - a)
;
; Blending reads what is behind, so the windows are drawn after everything opaque, from the
; farthest to the nearest. Drawn in the wrong order, a near window would be in the depth buffer
; first and hide the ones behind it.
;
; The grass and windows are sampled with clamp rather than wrap, so the top row of each image is
; not blended with its bottom row. In D3D a sampler is an object of its own, bound to a slot like a
; texture: this is a matter of binding a different one into s0 for those draws.
include rt.inc
include d3d11.inc
include macros.asm
include math.asm
include learn\window.asm
include learn\shader.asm
include learn\gpu.asm
include learn\cube.asm
include learn\camera.asm
include learn\transforms.asm

;---------------------------------------------------------------------------------------------------
.data
;---------------------------------------------------------------------------------------------------
align 4
plane_vertices real4 -5.0, -0.5,  5.0,   0.0, 1.0, 0.0,   0.0, 2.0
               real4  5.0, -0.5,  5.0,   0.0, 1.0, 0.0,   2.0, 2.0
               real4  5.0, -0.5, -5.0,   0.0, 1.0, 0.0,   2.0, 0.0
               real4 -5.0, -0.5,  5.0,   0.0, 1.0, 0.0,   0.0, 2.0
               real4  5.0, -0.5, -5.0,   0.0, 1.0, 0.0,   2.0, 0.0
               real4 -5.0, -0.5, -5.0,   0.0, 1.0, 0.0,   0.0, 0.0
plane_vertices_size equ 6 * 32

; A unit square standing on its bottom edge, facing +z: one blade of grass or one window.
quad_vertices real4 -0.5, 0.0, 0.0,   0.0, 0.0, 1.0,   0.0, 1.0
              real4  0.5, 0.0, 0.0,   0.0, 0.0, 1.0,   1.0, 1.0
              real4  0.5, 1.0, 0.0,   0.0, 0.0, 1.0,   1.0, 0.0
              real4 -0.5, 0.0, 0.0,   0.0, 0.0, 1.0,   0.0, 1.0
              real4  0.5, 1.0, 0.0,   0.0, 0.0, 1.0,   1.0, 0.0
              real4 -0.5, 1.0, 0.0,   0.0, 0.0, 1.0,   0.0, 0.0
quad_vertices_size equ 6 * 32

window_positions vec3 <-1.5, -0.5, -0.48>
                 vec3 <1.5, -0.5, 0.51>
                 vec3 <0.0, -0.5, 0.7>
                 vec3 <-0.3, -0.5, -2.3>
                 vec3 <0.5, -0.5, -0.6>
grass_positions vec3 <-2.2, -0.5, 0.4>
                vec3 <2.6, -0.5, 1.2>
                vec3 <0.6, -0.5, 1.4>
                vec3 <-0.6, -0.5, -1.8>

camera_start vec3 <0.0, 0.5, 4.5>
cube_a vec3 <-1.0, 0.0, -1.0>
cube_b vec3 <2.0, 0.0, 0.0>

align 16
t transforms <>
model mat4 <>
s shader <>
cam camera <>
offset_to_window vec3 <>
window_order dword 5 dup(?)     ; the windows, farthest first
window_distance real4 5 dup(?)
align 8
cube_vb qword 0
plane_vb qword 0
quad_vb qword 0
transforms_buffer qword 0
cube_texture qword 0
floor_texture qword 0
grass qword 0
glass qword 0
sampler qword 0
clamp_sampler qword 0
alpha_blend qword 0

; timing
delta_time real4 0.0
last_frame real4 0.0

;---------------------------------------------------------------------------------------------------
.code
;---------------------------------------------------------------------------------------------------
; -> rax: a blend state for transparency: out = src * src_alpha + dst * (1 - src_alpha)
align 16
create_alpha_blend proc
    local desc:D3D11_BLEND_DESC, state:qword
    invoke mem_zero, ADDR desc, sizeof D3D11_BLEND_DESC
    mov desc.RenderTarget.BlendEnable, 1
    mov desc.RenderTarget.SrcBlend, D3D11_BLEND_SRC_ALPHA
    mov desc.RenderTarget.DestBlend, D3D11_BLEND_INV_SRC_ALPHA
    mov desc.RenderTarget.BlendOp, D3D11_BLEND_OP_ADD
    mov desc.RenderTarget.SrcBlendAlpha, D3D11_BLEND_ONE
    mov desc.RenderTarget.DestBlendAlpha, D3D11_BLEND_INV_SRC_ALPHA
    mov desc.RenderTarget.BlendOpAlpha, D3D11_BLEND_OP_ADD
    mov desc.RenderTarget.RenderTargetWriteMask, D3D11_COLOR_WRITE_ENABLE_ALL
    vcall device, ID3D11Device_CreateBlendState, ADDR desc, ADDR state
    AssertHr
    mov rax, state
    ret
create_alpha_blend endp

; Puts the windows in window_order from the farthest from the camera to the nearest.
align 16
sort_windows proc
    USING rbx, rsi, rdi, r12
    SaveRegs
    ; A global indexed by a register needs its address in a register too: [label + rbx * 4] would
    ; be a 32-bit absolute address, which a 64-bit program loaded above 4 GB cannot use.
    lea rdi, window_order
    lea r12, window_distance
    xor ebx, ebx
    distances:
        mov [rdi + rbx * 4], ebx
        imul eax, ebx, sizeof vec3
        lea rdx, window_positions
        add rdx, rax
        invoke v3_sub, ADDR offset_to_window, rdx, ADDR cam.position
        invoke v3_length, ADDR offset_to_window
        movss real4 ptr [r12 + rbx * 4], xmm0
        inc ebx
        cmp ebx, 5
        jb distances
    ; selection sort on the distances, carrying the order along
    xor ebx, ebx
    outer:
        lea esi, [ebx + 1]
        inner:
            cmp esi, 5
            jae inner_done
            movss xmm0, real4 ptr [r12 + rsi * 4]
            comiss xmm0, real4 ptr [r12 + rbx * 4]
            jbe in_order
                movss xmm1, real4 ptr [r12 + rbx * 4]
                movss real4 ptr [r12 + rbx * 4], xmm0
                movss real4 ptr [r12 + rsi * 4], xmm1
                mov eax, [rdi + rbx * 4]
                mov edx, [rdi + rsi * 4]
                mov [rdi + rbx * 4], edx
                mov [rdi + rsi * 4], eax
            in_order:
            inc esi
            jmp inner
        inner_done:
        inc ebx
        cmp ebx, 4
        jb outer
    RestoreRegs
    ret
sort_windows endp

align 16
main proc
    USING rbx, rsi
    SaveRegs
    invoke window_create, "LearnD3D11"
    call window_create_depth

    invoke create_sampler, D3D11_FILTER_ANISOTROPIC, D3D11_TEXTURE_ADDRESS_WRAP
    mov sampler, rax
    invoke create_sampler, D3D11_FILTER_ANISOTROPIC, D3D11_TEXTURE_ADDRESS_CLAMP
    mov clamp_sampler, rax
    vcall context, ID3D11DeviceContext_PSSetSamplers, 0, 1, ADDR sampler
    call create_alpha_blend
    mov alpha_blend, rax

    invoke shader_create, ADDR s, "src/4.advanced_d3d11/3.1.blending/blending.hlsl", ADDR cube_layout, cube_layout_count

    invoke create_buffer, D3D11_BIND_VERTEX_BUFFER, ADDR cube_vertices, cube_vertices_size
    mov cube_vb, rax
    invoke create_buffer, D3D11_BIND_VERTEX_BUFFER, ADDR plane_vertices, plane_vertices_size
    mov plane_vb, rax
    invoke create_buffer, D3D11_BIND_VERTEX_BUFFER, ADDR quad_vertices, quad_vertices_size
    mov quad_vb, rax
    invoke create_constant_buffer, sizeof transforms
    mov transforms_buffer, rax
    invoke filesystem_path, "resources/textures/marble.jpg"
    invoke load_texture, rax, 0
    mov cube_texture, rax
    invoke filesystem_path, "resources/textures/metal.png"
    invoke load_texture, rax, 0
    mov floor_texture, rax
    invoke filesystem_path, "resources/textures/grass.png"
    invoke load_texture, rax, 0
    mov grass, rax
    invoke filesystem_path, "resources/textures/window.png"
    invoke load_texture, rax, 0
    mov glass, rax

    lea rcx, cam
    lea rdx, camera_start
    movf xmm2, -90.0
    movf xmm3, -10.0
    call camera_create

    render_loop:
        call window_poll
        test eax, eax
        jz render_loop_done

        call get_time
        movss xmm1, xmm0
        subss xmm0, last_frame
        movss delta_time, xmm0
        movss last_frame, xmm1

        invoke camera_process_input, ADDR cam, ADDR delta_time

        invoke window_begin, floats(0.1, 0.1, 0.1, 1.0)
        vcall context, ID3D11DeviceContext_IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
        invoke bind_constants, 0, transforms_buffer
        invoke transforms_from_camera, ADDR t, ADDR cam
        invoke shader_use, ADDR s

        ; everything opaque first: the floor and two cubes
        invoke bind_texture, 0, floor_texture
        invoke m4_identity, ADDR model
        invoke draw_vertices, transforms_buffer, ADDR t, plane_vb, 6, ADDR model
        invoke bind_texture, 0, cube_texture
        invoke m4_translate, ADDR model, ADDR cube_a
        invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, ADDR model
        invoke m4_translate, ADDR model, ADDR cube_b
        invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, ADDR model

        ; grass: clipped, so in any order
        vcall context, ID3D11DeviceContext_PSSetSamplers, 0, 1, ADDR clamp_sampler
        invoke bind_texture, 0, grass
        lea rsi, grass_positions
        xor ebx, ebx
        blades:
            invoke m4_translate, ADDR model, rsi
            invoke draw_vertices, transforms_buffer, ADDR t, quad_vb, 6, ADDR model
            add rsi, sizeof vec3
            inc ebx
            cmp ebx, 4
            jb blades

        ; windows: blended, so the farthest first
        call sort_windows
        vcall context, ID3D11DeviceContext_OMSetBlendState, alpha_blend, 0, D3D11_DEFAULT_SAMPLE_MASK
        invoke bind_texture, 0, glass
        xor ebx, ebx
        windows:
            lea rax, window_order
            mov eax, [rax + rbx * 4]
            imul eax, eax, sizeof vec3
            lea rsi, window_positions
            add rsi, rax
            invoke m4_translate, ADDR model, rsi
            invoke draw_vertices, transforms_buffer, ADDR t, quad_vb, 6, ADDR model
            inc ebx
            cmp ebx, 5
            jb windows
        vcall context, ID3D11DeviceContext_OMSetBlendState, 0, 0, D3D11_DEFAULT_SAMPLE_MASK
        vcall context, ID3D11DeviceContext_PSSetSamplers, 0, 1, ADDR sampler

        call window_present
        jmp render_loop
    render_loop_done:

    RestoreRegs
    invoke ExitProcess, 0
main endp

end
Part of masm64-dx11, LearnOpenGL's lessons rewritten for Direct3D 11 in x64 assembly. Licensed CC BY-NC 4.0, after LearnOpenGL by Joey de Vries.