gamma_correction.asm

src/5.advanced_lighting/2.gamma_correction/gamma_correction.asm · 211 lines

;---------------------------------------------------------------------------------------------------
; 2. Gamma correction
;---------------------------------------------------------------------------------------------------
; Monitors do not show a pixel value of 0.5 as half the light of 1.0, but as about a fifth:
; brightness follows roughly value^2.2. Images are stored with the inverse curve applied (sRGB), so
; they look right on screen. Lighting, though, is maths on physical light and only adds up in
; linear space.
;
; Done properly, the round trip is:
;
;   1. texture -> linear   the texture is created as an _SRGB format, so sampling it decodes to
;                          linear for free
;   2. lighting in linear  where two lights really are twice one light
;   3. linear -> screen    the back buffer is written through an _SRGB render target view, which
;                          encodes on the way in
;
; GL switches this with GL_FRAMEBUFFER_SRGB; in D3D it is a format, chosen per view. With gamma
; correction the four lights fall off physically, as 1/d^2. Without it that looks far too steep,
; which is why uncorrected code fell back on a linear 1/d that is wrong but looks closer.
;
;   Tab   gamma corrected (1/d^2) / uncorrected (1/d)
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 label byte
    real4 -10.0, -0.5,  10.0,   0.0, 1.0, 0.0,    0.0, 10.0
    real4  10.0, -0.5,  10.0,   0.0, 1.0, 0.0,   10.0, 10.0
    real4  10.0, -0.5, -10.0,   0.0, 1.0, 0.0,   10.0,  0.0
    real4 -10.0, -0.5,  10.0,   0.0, 1.0, 0.0,    0.0, 10.0
    real4  10.0, -0.5, -10.0,   0.0, 1.0, 0.0,   10.0,  0.0
    real4 -10.0, -0.5, -10.0,   0.0, 1.0, 0.0,    0.0,  0.0
plane_vertices_size equ 6 * 32

; Mirrors `cbuffer lights` in gamma_correction.hlsl: four lights in a row along x, each brighter
; than the last.
lights_constants struct
    positions vec4 4 dup(<>)
    colors vec4 4 dup(<>)
    gamma dword ?
    pad dword 3 dup(?)
lights_constants ends

align 4
camera_start vec3 <0.0, 1.0, 4.0>

align 16
t transforms <>
lights lights_constants <>
light_positions vec4 <-3.0, -0.4, 0.0, 0.0>, <-1.0, -0.4, 0.0, 0.0>, <1.0, -0.4, 0.0, 0.0>, <3.0, -0.4, 0.0, 0.0>
light_colors vec4 <0.25, 0.25, 0.25, 0.0>, <0.5, 0.5, 0.5, 0.0>, <0.75, 0.75, 0.75, 0.0>, <1.0, 1.0, 1.0, 0.0>
lamp_color vec4 <1.0, 1.0, 1.0, 1.0>
model mat4 <>
s shader <>
lamp_shader shader <>
cam camera <>
align 8
cube_vb qword 0
plane_vb qword 0
transforms_buffer qword 0
light_buffer qword 0
lamp_buffer qword 0
wood_linear qword 0
wood_srgb qword 0
backbuffer_srgb_rtv qword 0
target qword 0
sampler qword 0

; timing
delta_time real4 0.0
last_frame real4 0.0

;---------------------------------------------------------------------------------------------------
.code
;---------------------------------------------------------------------------------------------------
align 16
main proc
    local srgb_view:D3D11_RENDER_TARGET_VIEW_DESC
    USING rbx, rsi
    SaveRegs
    invoke window_create, "LearnD3D11  (Tab: corrected / uncorrected)"
    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

    ; A second view of the same back buffer, as sRGB, for the corrected mode.
    invoke mem_zero, ADDR srgb_view, sizeof D3D11_RENDER_TARGET_VIEW_DESC
    mov srgb_view.Format, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
    mov srgb_view.ViewDimension, D3D11_RTV_DIMENSION_TEXTURE2D
    vcall device, ID3D11Device_CreateRenderTargetView, backbuffer, ADDR srgb_view, ADDR backbuffer_srgb_rtv
    AssertHr

    ; build and compile shaders
    ; -------------------------
    invoke shader_create, ADDR s, "src/5.advanced_lighting/2.gamma_correction/gamma_correction.hlsl", ADDR cube_layout, cube_layout_count
    invoke shader_create, ADDR lamp_shader, "src/5.advanced_lighting/2.gamma_correction/light_cube.hlsl", ADDR cube_layout, cube_layout_count

    ; set up vertex data and textures: the same image, read two ways
    ; --------------------------------------------------------------
    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_constant_buffer, sizeof transforms
    mov transforms_buffer, rax
    invoke create_constant_buffer, sizeof lights_constants
    mov light_buffer, rax
    invoke create_constant_buffer, sizeof vec4
    mov lamp_buffer, rax
    invoke filesystem_path, "resources/textures/wood.png"
    mov rbx, rax
    invoke load_texture, rbx, 0
    mov wood_linear, rax
    invoke load_texture, rbx, 1
    mov wood_srgb, rax

    invoke mem_copy, ADDR lights.positions, ADDR light_positions, 4 * sizeof vec4
    invoke mem_copy, ADDR lights.colors, ADDR light_colors, 4 * sizeof vec4

    lea rcx, cam
    lea rdx, camera_start
    movf xmm2, -90.0
    movf xmm3, -15.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
        ; mode 0 corrected, mode 1 uncorrected
        invoke window_mode, 2
        mov ecx, 1
        sub ecx, eax
        mov lights.gamma, ecx

        ; render, through the sRGB view when correcting
        ; ------
        mov rax, backbuffer_rtv
        cmp lights.gamma, 0
        je plain_target
            mov rax, backbuffer_srgb_rtv
        plain_target:
        mov target, rax
        vcall context, ID3D11DeviceContext_OMSetRenderTargets, 1, ADDR target, depth_dsv
        vcall context, ID3D11DeviceContext_RSSetViewports, 1, ADDR viewport
        vcall context, ID3D11DeviceContext_ClearRenderTargetView, target, floats(0.0, 0.0, 0.0, 1.0)
        movf xmm3, 1.0                          ; the depth, a float: xmm3
        vcall context, ID3D11DeviceContext_ClearDepthStencilView, depth_dsv, D3D11_CLEAR_DEPTH, 0, 0
        vcall context, ID3D11DeviceContext_IASetPrimitiveTopology, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
        invoke bind_constants, 0, transforms_buffer
        invoke transforms_from_camera, ADDR t, ADDR cam

        invoke update_buffer, light_buffer, ADDR lights, sizeof lights_constants
        invoke shader_use, ADDR s
        invoke bind_constants, 1, light_buffer
        mov rax, wood_linear
        cmp lights.gamma, 0
        je plain_wood
            mov rax, wood_srgb
        plain_wood:
        invoke bind_texture, 0, rax
        invoke m4_identity, ADDR model
        invoke draw_vertices, transforms_buffer, ADDR t, plane_vb, 6, ADDR model

        ; a small lamp at each light
        invoke update_buffer, lamp_buffer, ADDR lamp_color, sizeof vec4
        invoke shader_use, ADDR lamp_shader
        invoke bind_constants, 1, lamp_buffer
        lea rsi, light_positions
        xor ebx, ebx
        lamps:
            lea rcx, model
            mov rdx, rsi
            movf xmm2, 0.05
            call m4_translate_scale
            invoke draw_vertices, transforms_buffer, ADDR t, cube_vb, 36, ADDR model
            add rsi, sizeof vec4
            inc ebx
            cmp ebx, 4
            jb lamps

        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.