point_shadows.asm

src/5.advanced_lighting/3.2.point_shadows/point_shadows.asm · 262 lines

;---------------------------------------------------------------------------------------------------
; 3.2 Point shadows
;---------------------------------------------------------------------------------------------------
; A point light shines in every direction, so one shadow map is not enough: it gets a cubemap of
; them, one face per axis. Each pixel looks up the face in the direction from the light to itself.
;
; Two changes from 3.1:
;
;   * one pass fills all six faces. The depth cubemap is bound as a six-slice array, and a geometry
;     shader copies every triangle six times, sending each copy to its slice with
;     SV_RenderTargetArrayIndex, through that face's view
;   * the map stores distance from the light, divided by the far plane, written by the pixel shader
;     through SV_Depth, rather than the projected depth. Comparing distances is the same in every
;     face
;
; The six face views are LearnOpenGL's, with one difference: a cubemap's faces are laid out
; left-handed, which a right-handed look-at cannot produce. GL's bottom-up framebuffer flips every
; rendered image and so fixes that for free; D3D's is top-down, so the face projection flips y
; instead (cube_face_matrices, includes/learn/shadow.asm). That flip reverses the winding, which is
; why culling stays off.
;
; The room is a large cube seen from inside, so its normals are flipped.
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
include learn\shadow.asm
include learn\srgb.asm

;---------------------------------------------------------------------------------------------------
.data
;---------------------------------------------------------------------------------------------------
; Mirror `cbuffer shadow` and `cbuffer surface` in point_shadows.hlsl.
shadow_constants struct
    faces mat4 6 dup(<>)        ; view-projection of each cube face
    light_position vec3 <>
    far_plane real4 ?
shadow_constants ends

surface_constants struct
    flip_normals real4 ?        ; -1 inside the room
    pad real4 3 dup(?)
surface_constants ends

shadow_size equ 1024

align 4
camera_start vec3 <3.0, 0.0, 2.0>
tilt_axis vec3 <1.0, 0.0, 1.0>
cube_place_a vec3 <4.0, -3.5, 0.0>
cube_place_b vec3 <2.0, 3.0, 1.0>
cube_place_c vec3 <-3.0, -1.0, 0.0>
cube_place_d vec3 <-1.5, 1.0, 1.5>
cube_place_e vec3 <-1.5, 2.0, -3.0>
room_size vec3 <10.0, 10.0, 10.0>
light_position vec3 <>

align 16
t transforms <>
sh shadow_constants <>
surface surface_constants <>
cubes mat4 5 dup(<>)
room mat4 <>
model mat4 <>
scratch mat4 <>
lamp_color vec4 <1.0, 1.0, 1.0, 1.0>
s shader <>
depth_shader shader <>
lamp_shader shader <>
cam camera <>
depth_map shadow_map <>
align 8
cube_vb qword 0
transforms_buffer qword 0
shadow_buffer qword 0
surface_buffer qword 0
lamp_buffer qword 0
wood qword 0
sampler qword 0
compare_sampler qword 0

; timing
current_frame real4 0.0
delta_time real4 0.0
last_frame real4 0.0

;---------------------------------------------------------------------------------------------------
.code
;---------------------------------------------------------------------------------------------------
; The five cubes inside the room, with whatever shader is bound.
align 16
draw_cubes proc
    USING rbx, rsi
    SaveRegs
    lea rsi, cubes
    xor ebx, ebx
    each_cube:
        invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, rsi
        add rsi, sizeof mat4
        inc ebx
        cmp ebx, 5
        jb each_cube
    RestoreRegs
    ret
draw_cubes endp

align 16
main proc
    invoke window_create, "LearnD3D11"
    ; The back buffer is viewed as sRGB: what the shaders write is linear, and the view
    ; gamma-encodes it on the way in (5.2).
    call window_use_srgb
    call window_create_depth

    invoke create_sampler, D3D11_FILTER_ANISOTROPIC, D3D11_TEXTURE_ADDRESS_WRAP
    mov sampler, rax
    vcall context, ID3D11DeviceContext_PSSetSamplers, 0, 1, ADDR sampler

    ; build and compile shaders
    ; -------------------------
    invoke shader_create, ADDR s, "src/5.advanced_lighting/3.2.point_shadows/point_shadows.hlsl", ADDR cube_layout, cube_layout_count
    invoke shader_create, ADDR depth_shader, "src/5.advanced_lighting/3.2.point_shadows/point_shadows_depth.hlsl", ADDR cube_layout, cube_layout_count
    invoke shader_create, ADDR lamp_shader, "src/5.advanced_lighting/3.2.point_shadows/light_cube.hlsl", ADDR cube_layout, cube_layout_count

    ; set up vertex data and textures
    ; -------------------------------
    invoke create_buffer, D3D11_BIND_VERTEX_BUFFER, ADDR cube_vertices, cube_vertices_size
    mov cube_vb, rax
    invoke create_constant_buffer, sizeof transforms
    mov transforms_buffer, rax
    invoke create_constant_buffer, sizeof shadow_constants
    mov shadow_buffer, rax
    invoke create_constant_buffer, sizeof surface_constants
    mov surface_buffer, rax
    invoke create_constant_buffer, sizeof vec4
    mov lamp_buffer, rax
    invoke filesystem_path, "resources/textures/wood.png"
    invoke load_texture, rax, 1
    mov wood, rax
    invoke create_depth_cube, ADDR depth_map, shadow_size
    call create_shadow_sampler
    mov compare_sampler, rax

    lea rcx, cam
    lea rdx, camera_start
    movf xmm2, -165.0
    movf xmm3, 0.0
    call camera_create

    ; five cubes around the room, and the room itself
    invoke m4_translate, ADDR cubes[0 * sizeof mat4], ADDR cube_place_a
    lea rcx, cubes[1 * sizeof mat4]
    lea rdx, cube_place_b
    movf xmm2, 1.5
    call m4_translate_scale
    invoke m4_translate, ADDR cubes[2 * sizeof mat4], ADDR cube_place_c
    invoke m4_translate, ADDR cubes[3 * sizeof mat4], ADDR cube_place_d
    ; translate(-1.5, 2, -3) * rotate(60 degrees, (1, 0, 1)) * scale(1.5)
    movf xmm0, 60.0
    call radians
    movaps xmm1, xmm0
    lea rcx, model
    lea r8, tilt_axis
    call m4_rotate
    invoke m4_identity, ADDR scratch
    storef scratch.m[0], 1.5
    storef scratch.m[20], 1.5
    storef scratch.m[40], 1.5
    invoke m4_mul, ADDR scratch, ADDR model, ADDR scratch
    invoke m4_translate, ADDR model, ADDR cube_place_e
    invoke m4_mul, ADDR cubes[4 * sizeof mat4], ADDR model, ADDR scratch
    invoke m4_scale, ADDR room, ADDR room_size
    storef sh.far_plane, 25.0

    render_loop:
        call window_poll
        test eax, eax
        jz render_loop_done

        ; per-frame time logic
        call get_time
        movss current_frame, xmm0
        movss xmm1, xmm0
        subss xmm0, last_frame
        movss delta_time, xmm0
        movss last_frame, xmm1

        invoke camera_process_input, ADDR cam, ADDR delta_time

        ; move light position over time: (0, 0, 3 sin(t / 2))
        movss xmm0, current_frame
        mulss xmm0, math_half
        call sinf
        movf xmm1, 3.0
        mulss xmm0, xmm1
        mov dword ptr light_position.x, 0
        mov dword ptr light_position.y, 0
        movss light_position.z, xmm0
        lea rcx, sh.faces
        lea rdx, light_position
        movf xmm2, 0.1
        movf xmm3, 25.0
        call cube_face_matrices
        v3load xmm0, light_position
        v3store sh.light_position, xmm0
        invoke update_buffer, shadow_buffer, ADDR sh, sizeof shadow_constants
        vcall context, ID3D11DeviceContext_IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
        invoke bind_constants, 0, transforms_buffer
        invoke bind_constants, 1, shadow_buffer

        ; 1. render scene to depth cubemap: all six faces in one pass
        ; -----------------------------------------------------------
        call unbind_textures
        vcall context, ID3D11DeviceContext_OMSetRenderTargets, 0, 0, depth_map.dsv
        movf xmm3, 1.0                          ; the depth, a float: xmm3
        vcall context, ID3D11DeviceContext_ClearDepthStencilView, depth_map.dsv, D3D11_CLEAR_DEPTH, 0, 0
        invoke set_viewport, shadow_size, shadow_size
        invoke shader_use, ADDR depth_shader
        invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, ADDR room
        call draw_cubes

        ; 2. render scene as normal
        ; -------------------------
        invoke window_begin, floats(0.1, 0.1, 0.1, 1.0)
        invoke transforms_from_camera, ADDR t, ADDR cam
        invoke shader_use, ADDR s
        invoke bind_constants, 2, surface_buffer
        invoke bind_texture, 0, wood
        invoke bind_texture, 1, depth_map.srv
        vcall context, ID3D11DeviceContext_PSSetSamplers, 1, 1, ADDR compare_sampler
        ; the room, seen from inside, then the cubes
        storef surface.flip_normals, -1.0
        invoke update_buffer, surface_buffer, ADDR surface, sizeof surface_constants
        invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, ADDR room
        storef surface.flip_normals, 1.0
        invoke update_buffer, surface_buffer, ADDR surface, sizeof surface_constants
        call draw_cubes

        ; the lamp
        invoke update_buffer, lamp_buffer, ADDR lamp_color, sizeof vec4
        invoke shader_use, ADDR lamp_shader
        invoke bind_constants, 1, lamp_buffer
        lea rcx, model
        lea rdx, light_position
        movf xmm2, 0.2
        call m4_translate_scale
        invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, ADDR model

        call window_present
        jmp render_loop
    render_loop_done:

    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.