diff --git a/src/renderer/dx12/dx12_renderer.cpp b/src/renderer/dx12/dx12_renderer.cpp index ec61534..31c06f8 100644 --- a/src/renderer/dx12/dx12_renderer.cpp +++ b/src/renderer/dx12/dx12_renderer.cpp @@ -11,7 +11,34 @@ void cg::renderer::dx12_renderer::init() { - // TODO Lab: 3.01 Add `model` and `camera` creation code into `init` method of `dx12_renderer` class + model = std::make_shared(); + model->load_obj(settings->model_path); + + camera = std::make_shared(); + camera->set_height(static_cast(settings->height)); + camera->set_width(static_cast(settings->width)); + camera->set_position(float3{ + settings->camera_position[0], + settings->camera_position[1], + settings->camera_position[2], + }); + + camera->set_theta(settings->camera_theta); + camera->set_phi(settings->camera_phi); + camera->set_angle_of_view(settings->camera_angle_of_view); + camera->set_z_near(settings->camera_z_near); + camera->set_z_far(settings->camera_z_far); + + view_port = CD3DX12_VIEWPORT(0.f, 0.f, + static_cast(settings->width), + static_cast(settings->height)); + + scissor_rect = CD3DX12_RECT(0, 0, + static_cast(settings->width), + static_cast(settings->height)); + + load_pipeline(); + load_assets(); } void cg::renderer::dx12_renderer::destroy() @@ -22,40 +49,112 @@ void cg::renderer::dx12_renderer::destroy() void cg::renderer::dx12_renderer::update() { - // TODO Lab: 3.08 Implement `update` method of `dx12_renderer` + auto now = std::chrono::high_resolution_clock::now(); + std::chrono::duration duration = now - current_time; + frame_duration = duration.count(); + current_time = now; + + cb.mwpMatrix = camera->get_dxm_mvp_matrix(); + memcpy(constant_buffer_data_begin, &cb, sizeof(cb)); } void cg::renderer::dx12_renderer::render() { - // TODO Lab: 3.06 Implement `render` method + populate_command_list(); + + ID3D12CommandList* command_lists[] = {command_list.Get()}; + command_queue->ExecuteCommandLists(_countof(command_lists), command_lists); + + THROW_IF_FAILED(swap_chain->Present(0, 0)); + + move_to_next_frame(); } ComPtr cg::renderer::dx12_renderer::get_dxgi_factory() { - // TODO Lab: 3.02 Enable a validation layer - return nullptr; + UINT dxgi_factory_flags = 0; + +#ifdef _DEBUG + ComPtr debug_controller; + if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)))) + { + debug_controller->EnableDebugLayer(); + dxgi_factory_flags |= DXGI_CREATE_FACTORY_DEBUG; + } +#endif + ComPtr dxgi_factory; + THROW_IF_FAILED( + CreateDXGIFactory2(dxgi_factory_flags, IID_PPV_ARGS(&dxgi_factory))); + return dxgi_factory; } void cg::renderer::dx12_renderer::initialize_device(ComPtr& dxgi_factory) { - // TODO Lab: 3.02 Enumerate hardware adapters - // TODO Lab: 3.02 Create a device object + ComPtr hardware_adapter; + dxgi_factory->EnumAdapters1(0, &hardware_adapter); +#ifdef _DEBUG + DXGI_ADAPTER_DESC adapter_desc{}; + hardware_adapter->GetDesc(&adapter_desc); + OutputDebugString(adapter_desc.Description); + OutputDebugString(L"\n"); +#endif + THROW_IF_FAILED(D3D12CreateDevice(hardware_adapter.Get(), + D3D_FEATURE_LEVEL_11_0, + IID_PPV_ARGS(&device))); } void cg::renderer::dx12_renderer::create_direct_command_queue() { - // TODO Lab: 3.02 Create a command queue + D3D12_COMMAND_QUEUE_DESC queue_desc{}; + queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + THROW_IF_FAILED(device->CreateCommandQueue(&queue_desc, + IID_PPV_ARGS(&command_queue))); } void cg::renderer::dx12_renderer::create_swap_chain(ComPtr& dxgi_factory) { - // TODO Lab: 3.02 Create a swap chain and bind it to window + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc{}; + swap_chain_desc.BufferCount = frame_number; + swap_chain_desc.Height = settings->height; + swap_chain_desc.Width = settings->width; + swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swap_chain_desc.SampleDesc.Count = 1; + + ComPtr temp_swap_chain; + THROW_IF_FAILED(dxgi_factory->CreateSwapChainForHwnd( + command_queue.Get(), + cg::utils::window::get_hwnd(), + &swap_chain_desc, + nullptr, + nullptr, + &temp_swap_chain)); + + dxgi_factory->MakeWindowAssociation( + cg::utils::window::get_hwnd(), + DXGI_MWA_NO_ALT_ENTER); + temp_swap_chain.As(&swap_chain); + frame_index = swap_chain->GetCurrentBackBufferIndex(); } void cg::renderer::dx12_renderer::create_render_target_views() { - // TODO Lab: 3.04 Create a descriptor heap for render targets - // TODO Lab: 3.04 Create render target views + rtv_heap.create_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, frame_number); + for (UINT i = 0; i < frame_number; ++i) { + THROW_IF_FAILED(swap_chain->GetBuffer( + i, + IID_PPV_ARGS(&render_targets[i]))); + device->CreateRenderTargetView( + render_targets[i].Get(), + nullptr, + rtv_heap.get_cpu_descriptor_handle(i)); + + std::wstring name(L"Render target "); + name += std::to_wstring(i); + render_targets[i]->SetName(name.c_str()); + } } void cg::renderer::dx12_renderer::create_depth_buffer() @@ -64,19 +163,30 @@ void cg::renderer::dx12_renderer::create_depth_buffer() void cg::renderer::dx12_renderer::create_command_allocators() { - // TODO Lab: 3.06 Create command allocators and a command list + for (auto& command_allocator : command_allocators) { + THROW_IF_FAILED(device->CreateCommandAllocator( + D3D12_COMMAND_LIST_TYPE_DIRECT, + IID_PPV_ARGS(&command_allocator))); + } } void cg::renderer::dx12_renderer::create_command_list() { - // TODO Lab: 3.06 Create command allocators and a command list + THROW_IF_FAILED(device->CreateCommandList( + 0, D3D12_COMMAND_LIST_TYPE_DIRECT, + command_allocators[0].Get(), + pipeline_state.Get(), + IID_PPV_ARGS(&command_list))); } void cg::renderer::dx12_renderer::load_pipeline() { - // TODO Lab: 3.02 Bring everything together in `load_pipeline` method - // TODO Lab: 3.04 Create render target views + ComPtr dxgi_factory = get_dxgi_factory(); + initialize_device(dxgi_factory); + create_direct_command_queue(); + create_swap_chain(dxgi_factory); + create_render_target_views(); } D3D12_STATIC_SAMPLER_DESC cg::renderer::dx12_renderer::get_sampler_descriptor() @@ -87,30 +197,138 @@ D3D12_STATIC_SAMPLER_DESC cg::renderer::dx12_renderer::get_sampler_descriptor() void cg::renderer::dx12_renderer::create_root_signature(const D3D12_STATIC_SAMPLER_DESC* sampler_descriptors, UINT num_sampler_descriptors) { - // TODO Lab: 3.05 Create a descriptor table and a root signature + CD3DX12_ROOT_PARAMETER1 root_parameters[1]; + CD3DX12_DESCRIPTOR_RANGE1 ranges[1]; + + ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, + 1, 0, 0, + D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); + root_parameters[0].InitAsDescriptorTable(1, &ranges[0], + D3D12_SHADER_VISIBILITY_ALL); + + D3D12_FEATURE_DATA_ROOT_SIGNATURE rs_feature_data{}; + rs_feature_data.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1; + if (FAILED(device->CheckFeatureSupport( + D3D12_FEATURE_ROOT_SIGNATURE, + &rs_feature_data, + sizeof(rs_feature_data)))) { + rs_feature_data.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0; + } + + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rs_desc; + rs_desc.Init_1_1( + _countof(root_parameters), + root_parameters, + num_sampler_descriptors, + sampler_descriptors, + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); + + ComPtr signature, error; + + HRESULT result = D3DX12SerializeVersionedRootSignature(&rs_desc, + rs_feature_data.HighestVersion, + &signature, + &error); + + if (FAILED(result)) { + OutputDebugStringA((char*) error->GetBufferPointer()); + THROW_IF_FAILED(result); + } + + THROW_IF_FAILED(device->CreateRootSignature(0, + signature->GetBufferPointer(), + signature->GetBufferSize(), + IID_PPV_ARGS(&root_signature))); } std::filesystem::path cg::renderer::dx12_renderer::get_shader_path(const std::string& shader_name) { - // TODO Lab: 3.05 Compile shaders - return ""; + WCHAR buffer[MAX_PATH]; + GetModuleFileName(nullptr, buffer, MAX_PATH); + return std::filesystem::path(buffer).parent_path() / shader_name; } ComPtr cg::renderer::dx12_renderer::compile_shader(const std::filesystem::path& shader_path, const std::string& entrypoint, const std::string& target) { - // TODO Lab: 3.05 Compile shaders - return nullptr; + ComPtr shader, error; + UINT compile_flags = 0; + +#ifdef _DEBUG + compile_flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + HRESULT result = D3DCompileFromFile( + shader_path.wstring().c_str(), + nullptr, + nullptr, + entrypoint.c_str(), + target.c_str(), + compile_flags, + 0, + &shader, + &error); + + if (FAILED(result)) { + OutputDebugStringA((char*) error->GetBufferPointer()); + THROW_IF_FAILED(result); + } + return shader; } void cg::renderer::dx12_renderer::create_pso(const std::string& shader_name) { - // TODO Lab: 3.05 Compile shaders - // TODO Lab: 3.05 Setup a PSO descriptor and create a PSO + ComPtr vertex_shader = compile_shader( + get_shader_path(shader_name), "VSMain", "vs_5_0"); + ComPtr pixel_shader = compile_shader( + get_shader_path(shader_name), "PSMain", "ps_5_0"); + + D3D12_INPUT_ELEMENT_DESC input_descriptors[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, + 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, + 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, + 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, + 0, 32, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 1, DXGI_FORMAT_R32G32B32_FLOAT, + 0, 44, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 2, DXGI_FORMAT_R32G32B32_FLOAT, + 0, 56, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + }; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc{}; + pso_desc.InputLayout = {input_descriptors, _countof(input_descriptors)}; + pso_desc.pRootSignature = root_signature.Get(); + pso_desc.VS = CD3DX12_SHADER_BYTECODE(vertex_shader.Get()); + pso_desc.PS = CD3DX12_SHADER_BYTECODE(pixel_shader.Get()); + pso_desc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); + pso_desc.RasterizerState.FrontCounterClockwise = TRUE; + pso_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; + pso_desc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); + pso_desc.DepthStencilState.DepthEnable = FALSE; + pso_desc.DepthStencilState.StencilEnable = FALSE; + pso_desc.SampleMask = UINT_MAX; + pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + pso_desc.NumRenderTargets = 1; + pso_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + pso_desc.SampleDesc.Count = 1; + + THROW_IF_FAILED(device->CreateGraphicsPipelineState(&pso_desc, IID_PPV_ARGS(&pipeline_state))); } void cg::renderer::dx12_renderer::create_resource_on_upload_heap(ComPtr& resource, UINT size, const std::wstring& name) { - // TODO Lab: 3.03 Implement resource creation on upload heap + THROW_IF_FAILED(device->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), + D3D12_HEAP_FLAG_NONE, + &CD3DX12_RESOURCE_DESC::Buffer(size), + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&resource))); + + if (!name.empty()) { + resource->SetName(name.c_str()); + } } void cg::renderer::dx12_renderer::create_resource_on_default_heap(ComPtr& resource, UINT size, const std::wstring& name, D3D12_RESOURCE_DESC* resource_descriptor) @@ -119,7 +337,14 @@ void cg::renderer::dx12_renderer::create_resource_on_default_heap(ComPtr& destination_resource) { - // TODO Lab: 3.03 Implement map, unmap, and copying data to the resource + UINT8* buffer_data_begin; + CD3DX12_RANGE read_range(0, 0); + THROW_IF_FAILED( + destination_resource->Map(0, &read_range, + reinterpret_cast(&buffer_data_begin))); + + memcpy(buffer_data_begin, buffer_data, buffer_size); + destination_resource->Unmap(0, 0); } void cg::renderer::dx12_renderer::copy_data(const void* buffer_data, const UINT buffer_size, ComPtr& destination_resource, ComPtr& intermediate_resource, D3D12_RESOURCE_STATES state_after, int row_pitch, int slice_pitch) @@ -128,14 +353,20 @@ void cg::renderer::dx12_renderer::copy_data(const void* buffer_data, const UINT D3D12_VERTEX_BUFFER_VIEW cg::renderer::dx12_renderer::create_vertex_buffer_view(const ComPtr& vertex_buffer, const UINT vertex_buffer_size) { - // TODO Lab: 3.04 Create vertex buffer views - return D3D12_VERTEX_BUFFER_VIEW{}; + D3D12_VERTEX_BUFFER_VIEW view{}; + view.BufferLocation = vertex_buffer->GetGPUVirtualAddress(); + view.StrideInBytes = sizeof(vertex); + view.SizeInBytes = vertex_buffer_size; + return view; } D3D12_INDEX_BUFFER_VIEW cg::renderer::dx12_renderer::create_index_buffer_view(const ComPtr& index_buffer, const UINT index_buffer_size) { - // TODO Lab: 3.04 Create index buffer views - return D3D12_INDEX_BUFFER_VIEW{}; + D3D12_INDEX_BUFFER_VIEW view{}; + view.BufferLocation = index_buffer->GetGPUVirtualAddress(); + view.SizeInBytes = index_buffer_size; + view.Format = DXGI_FORMAT_R32_UINT; + return view; } void cg::renderer::dx12_renderer::create_shader_resource_view(const ComPtr& texture, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handler) @@ -144,64 +375,209 @@ void cg::renderer::dx12_renderer::create_shader_resource_view(const ComPtr& buffer, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handler) { - // TODO Lab: 3.04 Create a constant buffer view + D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc{}; + cbv_desc.BufferLocation = buffer->GetGPUVirtualAddress(); + cbv_desc.SizeInBytes = (sizeof(cb) + 255) & ~255; + + device->CreateConstantBufferView(&cbv_desc, cpu_handler); } void cg::renderer::dx12_renderer::load_assets() { - // TODO Lab: 3.05 Create a descriptor table and a root signature - // TODO Lab: 3.05 Setup a PSO descriptor and create a PSO - // TODO Lab: 3.06 Create command allocators and a command list - - // TODO Lab: 3.04 Create a descriptor heap for a constant buffer + create_root_signature(nullptr, 0); + create_pso("shaders.hlsl"); + create_command_allocators(); + create_command_list(); + + cbv_srv_heap.create_heap( + device, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + 1, + D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE); + + size_t num_shapes = model->get_vertex_buffers().size(); + vertex_buffers.resize(num_shapes); + vertex_buffer_views.resize(num_shapes); + index_buffers.resize(num_shapes); + index_buffer_views.resize(num_shapes); + for (size_t i = 0; i < num_shapes; ++i) { + // Vertex buffer + auto vertex_buffer_data = model->get_vertex_buffers()[i]; + const UINT vertex_buffer_size = static_cast( + vertex_buffer_data->get_size_in_bytes()); + std::wstring vertex_buffer_name(L"Vertex buffer "); + vertex_buffer_name += std::to_wstring(i); + + create_resource_on_upload_heap(vertex_buffers[i], + vertex_buffer_size, + vertex_buffer_name); + copy_data(vertex_buffer_data->get_data(), + vertex_buffer_size, + vertex_buffers[i]); + vertex_buffer_views[i] = + create_vertex_buffer_view(vertex_buffers[i], + vertex_buffer_size); + + // Index buffer + auto index_buffer_data = model->get_index_buffers()[i]; + const UINT index_buffer_size = static_cast( + index_buffer_data->get_size_in_bytes()); + std::wstring index_buffer_name(L"index buffer "); + index_buffer_name += std::to_wstring(i); + + create_resource_on_upload_heap(index_buffers[i], + index_buffer_size, + index_buffer_name); + + copy_data(index_buffer_data->get_data(), + index_buffer_size, + index_buffers[i]); + + index_buffer_views[i] = create_index_buffer_view( + index_buffers[i], + index_buffer_size); + } + + // Constans buffer + std::wstring const_buffer_name(L"Constant buffer"); + create_resource_on_upload_heap(constant_buffer, + 64 * 1024, + const_buffer_name); + copy_data(&cb, sizeof(cb), constant_buffer); + CD3DX12_RANGE read_range(0, 0); + THROW_IF_FAILED(constant_buffer->Map( + 0, &read_range, + reinterpret_cast( + &constant_buffer_data_begin))); + + create_constant_buffer_view(constant_buffer, + cbv_srv_heap.get_cpu_descriptor_handle(0)); + + + THROW_IF_FAILED(command_list->Close()); + + THROW_IF_FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence))); + fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (fence_event == nullptr) { + THROW_IF_FAILED(HRESULT_FROM_WIN32(GetLastError())); + } - // TODO Lab: 3.03 Allocate memory for vertex and index buffers - // TODO Lab: 3.03 Create committed resources for vertex, index and constant buffers on upload heap - // TODO Lab: 3.03 Copy resource data to suitable resources - // TODO Lab: 3.04 Create vertex buffer views - // TODO Lab: 3.04 Create index buffer views - - // TODO Lab: 3.04 Create a constant buffer view - - // TODO Lab: 3.07 Create a fence and fence event + wait_for_gpu(); } void cg::renderer::dx12_renderer::populate_command_list() { - // TODO Lab: 3.06 Implement `populate_command_list` method + // Reset + THROW_IF_FAILED(command_allocators[frame_index]->Reset()) + THROW_IF_FAILED(command_list->Reset( + command_allocators[frame_index].Get(), + pipeline_state.Get())); + + // Initial state + command_list->SetGraphicsRootSignature(root_signature.Get()); + ID3D12DescriptorHeap* heaps[] = { cbv_srv_heap.get() }; + command_list->SetDescriptorHeaps(_countof(heaps), heaps); + command_list->SetGraphicsRootDescriptorTable(0, + cbv_srv_heap.get_gpu_descriptor_handle(0)); + + command_list->RSSetViewports(1, &view_port); + command_list->RSSetScissorRects(1, &scissor_rect); + + command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + D3D12_RESOURCE_BARRIER begin_barriers[] = { + CD3DX12_RESOURCE_BARRIER::Transition( + render_targets[frame_index].Get(), + D3D12_RESOURCE_STATE_PRESENT, + D3D12_RESOURCE_STATE_RENDER_TARGET + ) + }; + command_list->ResourceBarrier(_countof(begin_barriers), begin_barriers); + + // Drawing + command_list->OMSetRenderTargets( + 1, &rtv_heap.get_cpu_descriptor_handle(frame_index), + FALSE, nullptr); + const float clear_color[] = {0.f, 0.f, 0.f, 1.f}; + command_list->ClearRenderTargetView( + rtv_heap.get_cpu_descriptor_handle(frame_index), + clear_color, 0, nullptr + ); + + for(size_t s = 0; s < model->get_vertex_buffers().size(); ++s) { + command_list->IASetVertexBuffers(0, 1, &vertex_buffer_views[s]); + command_list->IASetIndexBuffer(&index_buffer_views[s]); + command_list->DrawIndexedInstanced( + static_cast(model->get_index_buffers()[s]->get_number_of_elements()), + 1, 0, 0, 0); + } + + D3D12_RESOURCE_BARRIER end_barriers[] = { + CD3DX12_RESOURCE_BARRIER::Transition( + render_targets[frame_index].Get(), + D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PRESENT + ) + }; + command_list->ResourceBarrier(_countof(end_barriers), end_barriers); + + THROW_IF_FAILED(command_list->Close()); } void cg::renderer::dx12_renderer::move_to_next_frame() { - // TODO Lab: 3.07 Implement `move_to_next_frame` method + const UINT16 current_fence_value = fence_values[frame_index]; + THROW_IF_FAILED(command_queue->Signal(fence.Get(), current_fence_value)); + frame_index = swap_chain->GetCurrentBackBufferIndex(); + if (fence->GetCompletedValue() < fence_values[frame_index]) { + THROW_IF_FAILED(fence->SetEventOnCompletion(fence_values[frame_index], fence_event)); + WaitForSingleObjectEx(fence_event, INFINITE, FALSE); + } + fence_values[frame_index] = current_fence_value + 1; } void cg::renderer::dx12_renderer::wait_for_gpu() { - // TODO Lab: 3.07 Implement `wait_for_gpu` method + THROW_IF_FAILED(command_queue->Signal(fence.Get(), fence_values[frame_index])); + THROW_IF_FAILED(fence->SetEventOnCompletion( + fence_values[frame_index], + fence_event)); + WaitForSingleObjectEx(fence_event, INFINITE, FALSE); + fence_values[frame_index]++; } void cg::renderer::descriptor_heap::create_heap(ComPtr& device, D3D12_DESCRIPTOR_HEAP_TYPE type, UINT number, D3D12_DESCRIPTOR_HEAP_FLAGS flags) { - // TODO Lab: 3.04 Implement `create_heap`, `get_cpu_descriptor_handle`, `get_gpu_descriptor_handle`, and `get` methods of `cg::renderer::descriptor_heap` + D3D12_DESCRIPTOR_HEAP_DESC heap_desc{}; + heap_desc.NumDescriptors = number; + heap_desc.Type = type; + heap_desc.Flags = flags; + + THROW_IF_FAILED(device->CreateDescriptorHeap( + &heap_desc, + IID_PPV_ARGS(&heap))); + descriptor_size = device->GetDescriptorHandleIncrementSize(type); } D3D12_CPU_DESCRIPTOR_HANDLE cg::renderer::descriptor_heap::get_cpu_descriptor_handle(UINT index) const { - // TODO Lab: 3.04 Implement `create_heap`, `get_cpu_descriptor_handle`, `get_gpu_descriptor_handle`, and `get` methods of `cg::renderer::descriptor_heap` - return D3D12_CPU_DESCRIPTOR_HANDLE{}; + return CD3DX12_CPU_DESCRIPTOR_HANDLE( + heap->GetCPUDescriptorHandleForHeapStart(), + static_cast(index), + descriptor_size); } D3D12_GPU_DESCRIPTOR_HANDLE cg::renderer::descriptor_heap::get_gpu_descriptor_handle(UINT index) const { - // TODO Lab: 3.04 Implement `create_heap`, `get_cpu_descriptor_handle`, `get_gpu_descriptor_handle`, and `get` methods of `cg::renderer::descriptor_heap` - return D3D12_GPU_DESCRIPTOR_HANDLE{}; + return CD3DX12_GPU_DESCRIPTOR_HANDLE( + heap->GetGPUDescriptorHandleForHeapStart(), + static_cast(index), + descriptor_size); } ID3D12DescriptorHeap* cg::renderer::descriptor_heap::get() const { - // TODO Lab: 3.04 Implement `create_heap`, `get_cpu_descriptor_handle`, `get_gpu_descriptor_handle`, and `get` methods of `cg::renderer::descriptor_heap` - return nullptr; -} + return heap.Get(); +} \ No newline at end of file diff --git a/src/renderer/rasterizer/rasterizer.h b/src/renderer/rasterizer/rasterizer.h index a3fa7f8..8f3f40e 100644 --- a/src/renderer/rasterizer/rasterizer.h +++ b/src/renderer/rasterizer/rasterizer.h @@ -4,8 +4,6 @@ #include #include -#include -#include #include @@ -55,22 +53,32 @@ namespace cg::renderer std::shared_ptr> in_render_target, std::shared_ptr> in_depth_buffer) { - // TODO Lab: 1.02 Implement `set_render_target`, `set_viewport`, `clear_render_target` methods of `cg::renderer::rasterizer` class - // TODO Lab: 1.06 Adjust `set_render_target`, and `clear_render_target` methods of `cg::renderer::rasterizer` class to consume a depth buffer + if (in_render_target) { + render_target = in_render_target; + } + if (in_depth_buffer) { + depth_buffer = in_depth_buffer; + } } template inline void rasterizer::set_viewport(size_t in_width, size_t in_height) { - // TODO Lab: 1.02 Implement `set_render_target`, `set_viewport`, `clear_render_target` methods of `cg::renderer::rasterizer` class + width = in_width; + height = in_height; } template inline void rasterizer::clear_render_target( const RT& in_clear_value, const float in_depth) { - // TODO Lab: 1.02 Implement `set_render_target`, `set_viewport`, `clear_render_target` methods of `cg::renderer::rasterizer` class - // TODO Lab: 1.06 Adjust `set_render_target`, and `clear_render_target` methods of `cg::renderer::rasterizer` class to consume a depth buffer + for (size_t i = 0; i < render_target->get_number_of_elements(); ++i) { + render_target->item(i) = in_clear_value; + } + + for (size_t i = 0; i < depth_buffer->get_number_of_elements(); ++i) { + depth_buffer->item(i) = in_depth; + } } template @@ -90,23 +98,78 @@ namespace cg::renderer template inline void rasterizer::draw(size_t num_vertexes, size_t vertex_offset) { - // TODO Lab: 1.04 Implement `cg::world::camera` class - // TODO Lab: 1.05 Add `Rasterization` and `Pixel shader` stages to `draw` method of `cg::renderer::rasterizer` - // TODO Lab: 1.06 Add `Depth test` stage to `draw` method of `cg::renderer::rasterizer` + size_t vertex_id = vertex_offset; + while (vertex_id < vertex_offset + num_vertexes) { + std::vector vertices(3); + vertices[0] = vertex_buffer->item(index_buffer->item(vertex_id++)); + vertices[1] = vertex_buffer->item(index_buffer->item(vertex_id++)); + vertices[2] = vertex_buffer->item(index_buffer->item(vertex_id++)); + + for (auto& vertex: vertices) { + float4 coords{vertex.x, vertex.y, vertex.z, 1.f}; + auto processed_vertex = vertex_shader(coords, vertex); + + vertex.x = processed_vertex.first.x / processed_vertex.first.w; + vertex.y = processed_vertex.first.y / processed_vertex.first.w; + vertex.z = processed_vertex.first.z / processed_vertex.first.w; + + vertex.x = (vertex.x + 1.f) * width / 2.f; + vertex.y = (-vertex.y + 1.f) * height / 2.f; + } + + float2 vertex_a = float2{vertices[0].x, vertices[0].y}; + float2 vertex_b = float2{vertices[1].x, vertices[1].y}; + float2 vertex_c = float2{vertices[2].x, vertices[2].y}; + + float2 min_vertex = min(vertex_a, min(vertex_b, vertex_c)); + float2 bounding_box_begin = round(clamp(min_vertex, float2{0.f, 0.f}, + float2{static_cast(width - 1), static_cast(height - 1)})); + + float2 max_vertex = max(vertex_a, max(vertex_b, vertex_c)); + float2 bounding_box_end = round(clamp(max_vertex, float2{0.f, 0.f}, + float2{static_cast(width - 1), static_cast(height - 1)})); + + float edge = edge_function(vertex_a, vertex_b, vertex_c); + + for (float x = bounding_box_begin.x; x <= bounding_box_end.x; x += 1.f) { + for (float y = bounding_box_begin.y; y <= bounding_box_end.y; y += 1.f) { + float2 point{x, y}; + float edge0 = edge_function(vertex_a, vertex_b, point); + float edge1 = edge_function(vertex_b, vertex_c, point); + float edge2 = edge_function(vertex_c, vertex_a, point); + if (edge0 >= 0.f && edge1 >= 0.f && edge2 >= 0.f) { + size_t u_x = static_cast(x); + size_t u_y = static_cast(y); + + float u = edge1 / edge; + float v = edge2 / edge; + float w = edge0 / edge; + + float z = u * vertices[0].z + + v * vertices[1].z + + w * vertices[2].z; + + if (depth_test(z, u_x, u_y)) { + auto pixel_result = pixel_shader(vertices[0], z); + render_target->item(u_x, u_y) = RT::from_color(pixel_result); + depth_buffer->item(u_x, u_y) = z; + } + } + } + } + } } template inline float rasterizer::edge_function(float2 a, float2 b, float2 c) { - // TODO Lab: 1.05 Implement `cg::renderer::rasterizer::edge_function` method - return 0.f; + return (c.x - a.x) * (b.y - a.y) - (c.y - a.y) * (b.x - a.x); } template inline bool rasterizer::depth_test(float z, size_t x, size_t y) { - // TODO Lab: 1.06 Implement `depth_test` function of `cg::renderer::rasterizer` class if (!depth_buffer) { return true; diff --git a/src/renderer/rasterizer/rasterizer_renderer.cpp b/src/renderer/rasterizer/rasterizer_renderer.cpp index d1bd605..55f51cb 100644 --- a/src/renderer/rasterizer/rasterizer_renderer.cpp +++ b/src/renderer/rasterizer/rasterizer_renderer.cpp @@ -5,17 +5,74 @@ void cg::renderer::rasterization_renderer::init() { - // TODO Lab: 1.02 Implement image clearing & saving in `cg::renderer::rasterization_renderer` class - // TODO Lab: 1.03 Adjust `cg::renderer::rasterization_renderer` class to consume `cg::world::model` - // TODO Lab: 1.04 Setup an instance of camera `cg::world::camera` class in `cg::renderer::rasterization_renderer` - // TODO Lab: 1.06 Add depth buffer in `cg::renderer::rasterization_renderer` + rasterizer = std::make_shared< + cg::renderer::rasterizer>(); + rasterizer->set_viewport(settings->width, settings->height); + + render_target = std::make_shared>( + settings->width, settings->height); + + rasterizer->set_render_target(render_target); + + model = std::make_shared(); + model->load_obj(settings->model_path); + + camera = std::make_shared(); + camera->set_height(static_cast(settings->height)); + camera->set_width(static_cast(settings->width)); + camera->set_position(float3{ + settings->camera_position[0], + settings->camera_position[1], + settings->camera_position[2], + }); + + camera->set_theta(settings->camera_theta); + camera->set_phi(settings->camera_phi); + camera->set_angle_of_view(settings->camera_angle_of_view); + camera->set_z_near(settings->camera_z_near); + camera->set_z_far(settings->camera_z_far); + + depth_buffer = std::make_shared>( + settings->width, settings->height); + rasterizer->set_render_target(render_target, depth_buffer); } void cg::renderer::rasterization_renderer::render() { - // TODO Lab: 1.02 Implement image clearing & saving in `cg::renderer::rasterization_renderer` class - // TODO Lab: 1.04 Implement `vertex_shader` lambda for the instance of `cg::renderer::rasterizer` - // TODO Lab: 1.05 Implement `pixel_shader` lambda for the instance of `cg::renderer::rasterizer` - // TODO Lab: 1.03 Adjust `cg::renderer::rasterization_renderer` class to consume `cg::world::model` + float4x4 matrix = mul( + camera->get_projection_matrix(), + camera->get_view_matrix(), + model->get_world_matrix()); + rasterizer->vertex_shader = [&](float4 vertex, cg::vertex data) { + auto processed = mul(matrix, vertex); + return std::make_pair(processed, data); + }; + rasterizer->pixel_shader = [](cg::vertex data, float z) { + return cg::color{ + data.ambient_r, + data.ambient_g, + data.ambient_b, + }; + }; + + auto start = std::chrono::high_resolution_clock::now(); + rasterizer->clear_render_target({111, 5, 243}); + auto stop = std::chrono::high_resolution_clock::now(); + std::chrono::duration clear_duration = stop - start; + std::cout << "Clearing took " << clear_duration.count() << "ms\n"; + + start = std::chrono::high_resolution_clock::now(); + for (size_t shape_id = 0; shape_id < model->get_index_buffers().size(); ++shape_id) { + rasterizer->set_vertex_buffer(model->get_vertex_buffers()[shape_id]); + rasterizer->set_index_buffer(model->get_index_buffers()[shape_id]); + rasterizer->draw( + model->get_index_buffers()[shape_id]->get_number_of_elements(), + 0); + } + stop = std::chrono::high_resolution_clock::now(); + std::chrono::duration rendering_duration = stop - start; + std::cout << "Rendering took " << rendering_duration.count() << "ms\n"; + + utils::save_resource(*render_target, settings->result_path); } void cg::renderer::rasterization_renderer::destroy() {} diff --git a/src/renderer/raytracer/raytracer.h b/src/renderer/raytracer/raytracer.h index d3d05e0..e5f422d 100644 --- a/src/renderer/raytracer/raytracer.h +++ b/src/renderer/raytracer/raytracer.h @@ -54,7 +54,20 @@ namespace cg::renderer inline triangle::triangle( const VB& vertex_a, const VB& vertex_b, const VB& vertex_c) { - // TODO Lab: 2.02 Implement a constructor of `triangle` struct + a = float3{vertex_a.x, vertex_a.y, vertex_a.z}; + b = float3{vertex_b.x, vertex_b.y, vertex_b.z}; + c = float3{vertex_c.x, vertex_c.y, vertex_c.z}; + + ba = b - a; + ca = c - a; + + na = float3{vertex_a.nx, vertex_a.ny, vertex_a.nz}; + nb = float3{vertex_b.nx, vertex_b.ny, vertex_b.nz}; + nc = float3{vertex_c.nx, vertex_c.ny, vertex_c.nz}; + + ambient = float3{vertex_a.ambient_r, vertex_a.ambient_g, vertex_a.ambient_b}; + diffuse = float3{vertex_a.diffuse_r, vertex_a.diffuse_g, vertex_a.diffuse_b}; + emissive = float3{vertex_a.emissive_r, vertex_a.emissive_g, vertex_a.emissive_b}; } template @@ -122,42 +135,58 @@ namespace cg::renderer inline void raytracer::set_render_target( std::shared_ptr> in_render_target) { - // TODO Lab: 2.01 Implement `set_render_target`, `set_viewport`, and `clear_render_target` methods of `raytracer` class + render_target = in_render_target; } template inline void raytracer::set_viewport(size_t in_width, size_t in_height) { - // TODO Lab: 2.01 Implement `set_render_target`, `set_viewport`, and `clear_render_target` methods of `raytracer` class - // TODO Lab: 2.06 Add `history` resource in `raytracer` class + width = in_width; + height = in_height; + + history = std::make_shared>(width, height); } template inline void raytracer::clear_render_target( const RT& in_clear_value) { - // TODO Lab: 2.01 Implement `set_render_target`, `set_viewport`, and `clear_render_target` methods of `raytracer` class - // TODO Lab: 2.06 Add `history` resource in `raytracer` class + for (size_t i = 0; i < render_target->get_number_of_elements(); ++i) { + render_target->item(i) = in_clear_value; + history->item(i) = float3{0.f, 0.f, 0.f}; + } } template inline void raytracer::set_vertex_buffers(std::vector>> in_vertex_buffers) { - // TODO Lab: 2.02 Implement `set_vertex_buffers` and `set_index_buffers` of `raytracer` class + vertex_buffers = in_vertex_buffers; } template void raytracer::set_index_buffers(std::vector>> in_index_buffers) { - // TODO Lab: 2.02 Implement `set_vertex_buffers` and `set_index_buffers` of `raytracer` class + index_buffers = in_index_buffers; } template inline void raytracer::build_acceleration_structure() { - // TODO Lab: 2.02 Fill `triangles` vector in `build_acceleration_structure` of `raytracer` class - // TODO Lab: 2.05 Implement `build_acceleration_structure` method of `raytracer` class + for (size_t i = 0; i < index_buffers.size(); ++i) { + auto& index_buffer = index_buffers[i]; + auto& vertex_buffer = vertex_buffers[i]; + size_t index = 0; + aabb aabb; + while (index < index_buffer->get_number_of_elements()) { + triangle triangle( + vertex_buffer->item(index_buffer->item(index++)), + vertex_buffer->item(index_buffer->item(index++)), + vertex_buffer->item(index_buffer->item(index++))); + aabb.add_triangle(triangle); + } + acceleration_structures.push_back(aabb); + } } template @@ -165,52 +194,161 @@ namespace cg::renderer float3 position, float3 direction, float3 right, float3 up, size_t depth, size_t accumulation_num) { - // TODO Lab: 2.01 Implement `ray_generation` and `trace_ray` method of `raytracer` class - // TODO Lab: 2.06 Implement TAA in `ray_generation` method of `raytracer` class + for (size_t frame_id = 0; frame_id < accumulation_num; frame_id++) { + std::cout << "Tracing " << frame_id << "/" << accumulation_num << " frame\n"; + float2 jitter = get_jitter(frame_id); +#pragma omp parallel for + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + float u = (2.f * x + jitter.x) / static_cast(width) - 1.f; + float v = (2.f * y + jitter.y) / static_cast(height) - 1.f; + u *= static_cast(width) / static_cast(height); + float3 ray_direction = direction + right * u - up * v; + + ray primary_ray(position, ray_direction); + payload payload_var = trace_ray(primary_ray, depth); + + history->item(x, y) += sqrt(payload_var.color.to_float3() / accumulation_num); + if (frame_id + 1 == accumulation_num) { + render_target->item(x, y) = RT::from_float3(history->item(x, y)); + } + } + } + } } template inline payload raytracer::trace_ray( const ray& ray, size_t depth, float max_t, float min_t) const { - // TODO Lab: 2.01 Implement `ray_generation` and `trace_ray` method of `raytracer` class - // TODO Lab: 2.02 Adjust `trace_ray` method of `raytracer` class to traverse geometry and call a closest hit shader - // TODO Lab: 2.04 Adjust `trace_ray` method of `raytracer` to use `any_hit_shader` - // TODO Lab: 2.05 Adjust `trace_ray` method of `raytracer` class to traverse the acceleration structure - return payload{}; + if (depth == 0) { + return miss_shader(ray); + } + --depth; + + payload closest_hit_payload{}; + closest_hit_payload.t = max_t; + const triangle* closest_triangle = nullptr; + + for (auto& aabb: acceleration_structures) { + if (!aabb.aabb_test(ray)) { + continue; + } + for (auto& triangle: aabb.get_triangles()) { + payload payload_var = intersection_shader(triangle, ray); + + if (payload_var.t > min_t && payload_var.t < closest_hit_payload.t) { + closest_hit_payload = payload_var; + closest_triangle = ▵ + if (any_hit_shader) { + return any_hit_shader(ray, payload_var, triangle); + } + } + } + } + + if (closest_hit_payload.t < max_t) { + if (closest_hit_shader) { + return closest_hit_shader(ray, closest_hit_payload, *closest_triangle, depth); + } + } + + return miss_shader(ray); } template inline payload raytracer::intersection_shader( const triangle& triangle, const ray& ray) const { - // TODO Lab: 2.02 Implement an `intersection_shader` method of `raytracer` class - return payload{}; + payload payload_var{}; + payload_var.t = -1.f; + + float3 pvec = cross(ray.direction, triangle.ca); + float det = dot(triangle.ba, pvec); + if (det > -1e-8 && det < 1e-8) { + return payload_var; + } + + float inv_det = 1.f / det; + float3 tvec = ray.position - triangle.a; + float u = dot(tvec, pvec) * inv_det; + if (u < 0.f || u > 1.f) { + return payload_var; + } + + float3 qvec = cross(tvec, triangle.ba); + float v = dot(ray.direction, qvec) * inv_det; + if (v < 0.f || u + v > 1.f) { + return payload_var; + } + + payload_var.t = dot(triangle.ca, qvec) * inv_det; + payload_var.bary = float3{1.f - u - v, u, v}; + return payload_var; } template float2 raytracer::get_jitter(int frame_id) { - // TODO Lab: 2.06 Implement `get_jitter` method of `raytracer` class + float2 result{0.f, 0.f}; + + constexpr int base_x = 2; + int index = frame_id + 1; + float inv_base = 1.f / base_x; + float fraction = inv_base; + while (index > 0) { + result.x += static_cast(index % base_x) * fraction; + index /= base_x; + fraction *= inv_base; + } + + constexpr int base_y = 3; + index = frame_id + 1; + inv_base = 1.f / base_y; + fraction = inv_base; + while (index > 0) { + result.y += static_cast(index % base_y) * fraction; + index /= base_y; + fraction *= inv_base; + } + + return result - 0.5f; } template inline void aabb::add_triangle(const triangle triangle) { - // TODO Lab: 2.05 Implement `aabb` class + if (triangles.empty()) { + aabb_min = aabb_max = triangle.a; + } + + triangles.push_back(triangle); + + aabb_max = max(aabb_max, triangle.a); + aabb_max = max(aabb_max, triangle.b); + aabb_max = max(aabb_max, triangle.c); + + aabb_min = max(aabb_min, triangle.a); + aabb_min = max(aabb_min, triangle.b); + aabb_min = max(aabb_min, triangle.c); } template inline const std::vector>& aabb::get_triangles() const { - // TODO Lab: 2.05 Implement `aabb` class + return triangles; } template inline bool aabb::aabb_test(const ray& ray) const { - // TODO Lab: 2.05 Implement `aabb` class + float3 inv_ray_dir = 1.f / ray.direction; + float3 t0 = (aabb_max - ray.position) * inv_ray_dir; + float3 t1 = (aabb_min - ray.position) * inv_ray_dir; + float3 t_min = min(t0, t1); + float3 t_max = max(t0, t1); + return maxelem(t_min) <= maxelem(t_max); } }// namespace cg::renderer \ No newline at end of file diff --git a/src/renderer/raytracer/raytracer_renderer.cpp b/src/renderer/raytracer/raytracer_renderer.cpp index dda98bf..2bef31c 100644 --- a/src/renderer/raytracer/raytracer_renderer.cpp +++ b/src/renderer/raytracer/raytracer_renderer.cpp @@ -7,9 +7,32 @@ void cg::renderer::ray_tracing_renderer::init() { - // TODO Lab: 2.01 Add `render_target`, `camera`, and `raytracer` in `ray_tracing_renderer` class - // TODO Lab: 2.03 Add light information to `lights` array of `ray_tracing_renderer` - // TODO Lab: 2.04 Initialize `shadow_raytracer` in `ray_tracing_renderer` + render_target = std::make_shared>( + settings->width, settings->height); + + model = std::make_shared(); + model->load_obj(settings->model_path); + + camera = std::make_shared(); + camera->set_height(static_cast(settings->height)); + camera->set_width(static_cast(settings->width)); + camera->set_position(float3{ + settings->camera_position[0], + settings->camera_position[1], + settings->camera_position[2], + }); + + camera->set_theta(settings->camera_theta); + camera->set_phi(settings->camera_phi); + camera->set_angle_of_view(settings->camera_angle_of_view); + camera->set_z_near(settings->camera_z_near); + camera->set_z_far(settings->camera_z_far); + + raytracer = std::make_shared>(); + raytracer->set_viewport(settings->width, settings->height); + raytracer->set_render_target(render_target); + raytracer->set_vertex_buffers(model->get_vertex_buffers()); + raytracer->set_index_buffers(model->get_index_buffers()); } void cg::renderer::ray_tracing_renderer::destroy() {} @@ -18,11 +41,54 @@ void cg::renderer::ray_tracing_renderer::update() {} void cg::renderer::ray_tracing_renderer::render() { - // TODO Lab: 2.01 Implement `miss_shader`, image clearing, calling `ray_generation`, and saving in `ray_tracing_renderer` class - // TODO Lab: 2.02 Add `closest_hit_shader` to `raytracer` class to return diffuse color - // TODO Lab: 2.03 Adjust `closest_hit_shader` of `raytracer` to implement Lambertian shading model - // TODO Lab: 2.04 Define `any_hit_shader` and `miss_shader` for `shadow_raytracer` - // TODO Lab: 2.04 Adjust `closest_hit_shader` of `raytracer` to cast shadows rays and to ignore occluded lights - // TODO Lab: 2.05 Adjust `ray_tracing_renderer` class to build the acceleration structure + raytracer->miss_shader = [](const ray& ray) { + payload payload{}; + payload.color = {0.f, 0.f, 0.f}; + return payload; + }; + + std::random_device random_device; + std::mt19937 random_generator(random_device()); + std::uniform_real_distribution uni_dist(-1.f, 1.f); + + raytracer->closest_hit_shader = [&](const ray& ray, payload& payload, + const triangle& triangle, + size_t depth) { + float3 position = ray.position + ray.direction * payload.t; + float3 normal = payload.bary.x * triangle.na + + payload.bary.y * triangle.nb + + payload.bary.z * triangle.nc; + + float3 res_color = triangle.emissive; + + float3 rand_direction{uni_dist(random_generator), + uni_dist(random_generator), + uni_dist(random_generator)}; + if (dot(rand_direction, normal) < 0.f) { + rand_direction = -rand_direction; + } + + cg::renderer::ray to_rand_direction(position, rand_direction); + auto next_payload = raytracer->trace_ray(to_rand_direction, depth); + res_color += next_payload.color.to_float3() * triangle.diffuse * + std::max(0.f, dot(normal, to_rand_direction.direction)); + + payload.color = cg::color::from_float3(res_color); + return payload; + }; + + raytracer->build_acceleration_structure(); + raytracer->clear_render_target({0, 0, 0}); + + auto start = std::chrono::high_resolution_clock::now(); + raytracer->ray_generation( + camera->get_position(), camera->get_direction(), + camera->get_right(), camera->get_up(), + settings->raytracing_depth, settings->accumulation_num); + auto stop = std::chrono::high_resolution_clock::now(); + std::chrono::duration rt_duration = stop - start; + std::cout << "Ray tracing took " << rt_duration.count() << "ms\n"; + + cg::utils::save_resource(*render_target, settings->result_path); // TODO Lab: 2.06 (Bonus) Adjust `closest_hit_shader` for Monte-Carlo light tracing } \ No newline at end of file diff --git a/src/resource.h b/src/resource.h index 68bdec7..7b53a7d 100644 --- a/src/resource.h +++ b/src/resource.h @@ -36,66 +36,68 @@ namespace cg template inline resource::resource(size_t size) { - // TODO Lab: 1.02 Implement `cg::resource` class + data.resize(size); + stride = 0; } + template inline resource::resource(size_t x_size, size_t y_size) { - // TODO Lab: 1.02 Implement `cg::resource` class + data.resize(x_size * y_size); + stride = x_size; } + template inline resource::~resource() { } + template inline const T* resource::get_data() { - // TODO Lab: 1.02 Implement `cg::resource` class - return nullptr; + return data.data(); } + template inline T& resource::item(size_t item) { - // TODO Lab: 1.02 Implement `cg::resource` class - return T(); + return data.at(item); } + template inline T& resource::item(size_t x, size_t y) { - // TODO Lab: 1.02 Implement `cg::resource` class - return T(); + return data.at(stride * y + x); } + template inline size_t resource::get_size_in_bytes() const { - // TODO Lab: 1.02 Implement `cg::resource` class - return 0; + return data.size() * item_size; } + template inline size_t resource::get_number_of_elements() const { - // TODO Lab: 1.02 Implement `cg::resource` class - return 0; + return data.size(); } template inline size_t resource::get_stride() const { - // TODO Lab: 1.02 Implement `cg::resource` class - return 0; + return stride; } struct color { static color from_float3(const float3& in) { - // TODO Lab: 1.02 Implement `cg::color` and `cg::unsigned_color` structs - return color{}; + return color{in.x, in.y, in.z}; }; + float3 to_float3() const { - // TODO Lab: 1.02 Implement `cg::color` and `cg::unsigned_color` structs - return float3{}; + return float3{r, g, b}; } float r; float g; @@ -106,18 +108,28 @@ namespace cg { static unsigned_color from_color(const color& color) { - // TODO Lab: 1.02 Implement `cg::color` and `cg::unsigned_color` structs - return unsigned_color{}; + return unsigned_color{ + static_cast(std::clamp(color.r * 225.f, 0.f, 255.f)), + static_cast(std::clamp(color.g * 225.f, 0.f, 255.f)), + static_cast(std::clamp(color.b * 225.f, 0.f, 255.f)) + }; }; static unsigned_color from_float3(const float3& color) { - // TODO Lab: 1.02 Implement `cg::color` and `cg::unsigned_color` structs - return unsigned_color{}; + float3 clampled = clamp(255.f * color, 0.f, 255.f); + return unsigned_color{ + static_cast(clampled.x), + static_cast(clampled.y), + static_cast(clampled.z) + }; }; float3 to_float3() const { - // TODO Lab: 1.02 Implement `cg::color` and `cg::unsigned_color` structs - return float3{}; + return float3{ + static_cast(r), + static_cast(g), + static_cast(b), + } / 255.f; }; uint8_t r; uint8_t g; @@ -127,7 +139,28 @@ namespace cg struct vertex { - // TODO Lab: 1.03 Implement `cg::vertex` struct + float x; + float y; + float z; + + float nx; + float ny; + float nz; + + float u; + float v; + + float ambient_r; + float ambient_g; + float ambient_b; + + float diffuse_r; + float diffuse_g; + float diffuse_b; + + float emissive_r; + float emissive_g; + float emissive_b; }; }// namespace cg \ No newline at end of file diff --git a/src/utils/resource_utils.cpp b/src/utils/resource_utils.cpp index dc36d32..6497f5a 100644 --- a/src/utils/resource_utils.cpp +++ b/src/utils/resource_utils.cpp @@ -8,20 +8,18 @@ #include -using namespace cg::utils; - std::string view_command(const std::filesystem::path& path) { #ifdef __APPLE__ return std::string("open ").append(path.string()); #endif -#ifdef __WINDOWS__ +#ifdef _WIN32 return std::string("start ").append(path.string()); #endif - return path.string(); + return ""; } -void save_resource(cg::resource& render_target, const std::filesystem::path& filepath) +void cg::utils::save_resource(cg::resource& render_target, const std::filesystem::path& filepath) { int width = static_cast(render_target.get_stride()); int height = static_cast(render_target.get_number_of_elements()) / width; @@ -33,6 +31,8 @@ void save_resource(cg::resource& render_target, const std::f if (result != 1) THROW_ERROR("Can't save the resource"); - std::system(view_command(filepath).c_str()); + auto command = view_command(filepath); + if (!command.empty()) + std::system(command.c_str()); } diff --git a/src/utils/resource_utils.h b/src/utils/resource_utils.h index 9b83496..96fa322 100644 --- a/src/utils/resource_utils.h +++ b/src/utils/resource_utils.h @@ -7,5 +7,5 @@ namespace cg::utils { - void save_resource(cg::resource& render_target, std::filesystem::path filepath); + void save_resource(cg::resource& render_target, const std::filesystem::path& filepath); } \ No newline at end of file diff --git a/src/world/camera.cpp b/src/world/camera.cpp index c844527..835ddfe 100644 --- a/src/world/camera.cpp +++ b/src/world/camera.cpp @@ -19,106 +19,121 @@ cg::world::camera::~camera() {} void cg::world::camera::set_position(float3 in_position) { - // TODO Lab: 1.04 Implement `cg::world::camera` class + + position = in_position; } void cg::world::camera::set_theta(float in_theta) { - // TODO Lab: 1.04 Implement `cg::world::camera` class + theta = in_theta * static_cast(M_PI) / 180.f; } void cg::world::camera::set_phi(float in_phi) { - // TODO Lab: 1.04 Implement `cg::world::camera` class + phi = in_phi * static_cast(M_PI) / 180.f; } void cg::world::camera::set_angle_of_view(float in_aov) { - // TODO Lab: 1.04 Implement `cg::world::camera` class + angle_of_view = in_aov * static_cast(M_PI) / 180.f; } void cg::world::camera::set_height(float in_height) { - // TODO Lab: 1.04 Implement `cg::world::camera` class + height = in_height; + aspect_ratio = width / height; } void cg::world::camera::set_width(float in_width) { - // TODO Lab: 1.04 Implement `cg::world::camera` class + width = in_width; + aspect_ratio = width / height; } void cg::world::camera::set_z_near(float in_z_near) { - // TODO Lab: 1.04 Implement `cg::world::camera` class + z_near = in_z_near; } void cg::world::camera::set_z_far(float in_z_far) { - // TODO Lab: 1.04 Implement `cg::world::camera` class + z_far = in_z_far; } const float4x4 cg::world::camera::get_view_matrix() const { - // TODO Lab: 1.04 Implement `cg::world::camera` class - return float4x4{}; + float3 up{0.f, 1.f, 0.f}; + float3 eye = position + get_direction(); + + float3 z_axis = normalize(position - eye); + float3 x_axis = normalize(cross(up, z_axis)); + float3 y_axis = cross(z_axis, x_axis); + return float4x4{ + {x_axis.x, y_axis.x, z_axis.x, 0}, + {x_axis.y, y_axis.y, z_axis.y, 0}, + {x_axis.z, y_axis.z, z_axis.z, 0}, + {-dot(x_axis, position), -dot(y_axis, position), -dot(z_axis, position), 1}}; } #ifdef DX12 const DirectX::XMMATRIX cg::world::camera::get_dxm_view_matrix() const { - // TODO Lab: 3.08 Implement `get_dxm_view_matrix`, `get_dxm_projection_matrix`, and `get_dxm_mvp_matrix` methods of `camera` - return DirectX::XMMatrixIdentity(); + return DirectX::XMMatrixIdentity(); } const DirectX::XMMATRIX cg::world::camera::get_dxm_projection_matrix() const { - // TODO Lab: 3.08 Implement `get_dxm_view_matrix`, `get_dxm_projection_matrix`, and `get_dxm_mvp_matrix` methods of `camera` - return DirectX::XMMatrixIdentity(); + return DirectX::XMMatrixIdentity(); } const DirectX::XMMATRIX camera::get_dxm_mvp_matrix() const { - // TODO Lab: 3.08 Implement `get_dxm_view_matrix`, `get_dxm_projection_matrix`, and `get_dxm_mvp_matrix` methods of `camera` - return DirectX::XMMatrixIdentity(); + + return DirectX::XMMatrixIdentity(); } #endif const float4x4 cg::world::camera::get_projection_matrix() const { - // TODO Lab: 1.04 Implement `cg::world::camera` class - return float4x4{}; + float f = 1.f / tanf(angle_of_view / 2.f); + return float4x4{ + {f / aspect_ratio, 0, 0, 0}, + {0, f, 0, 0}, + {0, 0, z_far / (z_near - z_far), -1}, + {0, 0, (z_far * z_near) / (z_near - z_far), 0}}; } const float3 cg::world::camera::get_position() const { - // TODO Lab: 1.04 Implement `cg::world::camera` class - return float3{}; + return position; } const float3 cg::world::camera::get_direction() const { - // TODO Lab: 1.04 Implement `cg::world::camera` class - return float3{}; + + return float3{ + sin(theta) * cos(phi), + sin(phi), + -cos(theta) * cos(phi)}; } const float3 cg::world::camera::get_right() const { - // TODO Lab: 1.04 Implement `cg::world::camera` class - return float3{}; + return cross(get_direction(), float3(0.f, 1.f, 0.f)); } const float3 cg::world::camera::get_up() const { - // TODO Lab: 1.04 Implement `cg::world::camera` class - return float3{}; + + return cross(get_right(), get_direction()); } + const float camera::get_theta() const { - // TODO Lab: 1.04 Implement `cg::world::camera` class - return 0.f; + return theta; } + const float camera::get_phi() const { - // TODO Lab: 1.04 Implement `cg::world::camera` class - return 0.f; + return phi; } diff --git a/src/world/model.cpp b/src/world/model.cpp index 161c21d..c330551 100644 --- a/src/world/model.cpp +++ b/src/world/model.cpp @@ -4,6 +4,7 @@ #include "utils/error_handler.h" +#include #include @@ -16,42 +17,182 @@ cg::world::model::~model() {} void cg::world::model::load_obj(const std::filesystem::path& model_path) { - // TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, `fill_buffers`, `get_vertex_buffers`, `get_index_buffers` methods of `cg::world::model` class + tinyobj::ObjReaderConfig readerConfig; + readerConfig.mtl_search_path = model_path.parent_path().string(); + readerConfig.triangulate = true; + + tinyobj::ObjReader reader; + if (!reader.ParseFromFile(model_path.string(), readerConfig)) { + if (!reader.Error().empty()) { + THROW_ERROR(reader.Error()); + } + } + + auto& shapes = reader.GetShapes(); + auto& attrib = reader.GetAttrib(); + auto& materials = reader.GetMaterials(); + + allocate_buffers(shapes); + fill_buffers(shapes, attrib, materials, model_path.parent_path()); } void model::allocate_buffers(const std::vector& shapes) { - // TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, `fill_buffers`, `get_vertex_buffers`, `get_index_buffers` methods of `cg::world::model` class + for (const auto& shape: shapes) { + size_t index_offset = 0; + unsigned int vertex_buffer_size = 0; + unsigned int index_buffer_size = 0; + std::map, unsigned int> index_map; + const auto& mesh = shape.mesh; + + for (const auto& fv: mesh.num_face_vertices) { + for (size_t v = 0; v < fv; ++v) { + tinyobj::index_t idx = mesh.indices[index_offset + v]; + auto idx_tuple = std::make_tuple( + idx.vertex_index, + idx.normal_index, + idx.texcoord_index); + + if (index_map.count(idx_tuple) == 0) { + index_map[idx_tuple] = vertex_buffer_size; + ++vertex_buffer_size; + } + + index_buffer_size++; + } + index_offset += fv; + } + vertex_buffers.push_back(std::make_shared>(vertex_buffer_size)); + index_buffers.push_back(std::make_shared>(index_buffer_size)); + + std::cout << "Vertex buffer size " << vertex_buffer_size * sizeof(cg::vertex) << '\n'; + std::cout << "Index buffer size " << index_buffer_size * sizeof(unsigned int) << '\n'; + + std::cout << "Pure vertex buffer size " << index_buffer_size * sizeof(cg::vertex) << '\n'; + std::cout << "Saving " << index_buffer_size * sizeof(cg::vertex) - vertex_buffer_size * sizeof(cg::vertex) - index_buffer_size * sizeof(unsigned int) << '\n'; + } + textures.resize(shapes.size()); } float3 cg::world::model::compute_normal(const tinyobj::attrib_t& attrib, const tinyobj::mesh_t& mesh, size_t index_offset) { - // TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, `fill_buffers`, `get_vertex_buffers`, `get_index_buffers` methods of `cg::world::model` class - return float3{}; + auto a_id = mesh.indices[index_offset]; + auto b_id = mesh.indices[index_offset + 1]; + auto c_id = mesh.indices[index_offset + 2]; + + float3 a{ + attrib.vertices[3 * a_id.vertex_index], + attrib.vertices[3 * a_id.vertex_index + 1], + attrib.vertices[3 * a_id.vertex_index + 2], + }; + + float3 b{ + attrib.vertices[3 * b_id.vertex_index], + attrib.vertices[3 * b_id.vertex_index + 1], + attrib.vertices[3 * b_id.vertex_index + 2], + }; + + float3 c{ + attrib.vertices[3 * c_id.vertex_index], + attrib.vertices[3 * c_id.vertex_index + 1], + attrib.vertices[3 * c_id.vertex_index + 2], + }; + + return normalize(cross(b - a, c - a)); } void model::fill_vertex_data(cg::vertex& vertex, const tinyobj::attrib_t& attrib, const tinyobj::index_t idx, const float3 computed_normal, const tinyobj::material_t material) { - // TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, `fill_buffers`, `get_vertex_buffers`, `get_index_buffers` methods of `cg::world::model` class + vertex.x = attrib.vertices[3 * idx.vertex_index]; + vertex.y = attrib.vertices[3 * idx.vertex_index + 1]; + vertex.z = attrib.vertices[3 * idx.vertex_index + 2]; + + if (idx.normal_index < 0) { + vertex.nx = computed_normal.x; + vertex.ny = computed_normal.y; + vertex.nz = computed_normal.z; + } + else { + vertex.nx = attrib.normals[3 * idx.normal_index]; + vertex.ny = attrib.normals[3 * idx.normal_index + 1]; + vertex.nz = attrib.normals[3 * idx.normal_index + 2]; + } + + if (idx.texcoord_index < 0) { + vertex.u = 0.f; + vertex.v = 0.f; + } + else { + vertex.u = attrib.texcoords[2 * idx.texcoord_index]; + vertex.v = attrib.texcoords[2 * idx.texcoord_index + 1]; + } + + vertex.ambient_r = material.ambient[0]; + vertex.ambient_g = material.ambient[1]; + vertex.ambient_b = material.ambient[2]; + + vertex.diffuse_r = material.diffuse[0]; + vertex.diffuse_g = material.diffuse[1]; + vertex.diffuse_b = material.diffuse[2]; + + vertex.emissive_r = material.emission[0]; + vertex.emissive_g = material.emission[1]; + vertex.emissive_b = material.emission[2]; } void model::fill_buffers(const std::vector& shapes, const tinyobj::attrib_t& attrib, const std::vector& materials, const std::filesystem::path& base_folder) { - // TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, `fill_buffers`, `get_vertex_buffers`, `get_index_buffers` methods of `cg::world::model` class + for (size_t s = 0; s < shapes.size(); ++s) { + size_t index_offset = 0; + unsigned int vertex_buffer_id = 0; + unsigned int index_buffer_id = 0; + auto vertex_buffer = vertex_buffers[s]; + auto index_buffer = index_buffers[s]; + std::map, unsigned int> index_map; + const auto& mesh = shapes[s].mesh; + + for (size_t f = 0; f < mesh.num_face_vertices.size(); ++f) { + int fv = mesh.num_face_vertices[f]; + float3 normal; + if (mesh.indices[index_offset].normal_index < 0) { + normal = compute_normal(attrib, mesh, index_offset); + } + + for (size_t v = 0; v < fv; ++v) { + tinyobj::index_t idx = mesh.indices[index_offset + v]; + auto idx_tuple = std::make_tuple( + idx.vertex_index, + idx.normal_index, + idx.texcoord_index); + + if (index_map.count(idx_tuple) == 0) { + cg::vertex& vertex = vertex_buffer->item(vertex_buffer_id); + const auto& material = materials[mesh.material_ids[f]]; + fill_vertex_data(vertex, attrib, idx, normal, material); + index_map[idx_tuple] = vertex_buffer_id; + vertex_buffer_id++; + } + index_buffer->item(index_buffer_id) = index_map[idx_tuple]; + index_buffer_id++; + } + index_offset += fv; + } + if (!materials[mesh.material_ids[0]].diffuse_texname.empty()) { + textures[s] = base_folder / materials[mesh.material_ids[0]].diffuse_texname; + } + } } const std::vector>>& cg::world::model::get_vertex_buffers() const { - // TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, `fill_buffers`, `get_vertex_buffers`, `get_index_buffers` methods of `cg::world::model` class return vertex_buffers; } const std::vector>>& cg::world::model::get_index_buffers() const { - // TODO Lab: 1.03 Using `tinyobjloader` implement `load_obj`, `allocate_buffers`, `compute_normal`, `fill_vertex_data`, `fill_buffers`, `get_vertex_buffers`, `get_index_buffers` methods of `cg::world::model` class return index_buffers; }