diff --git a/README.md b/README.md index 06c6517..f38ba45 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Khaotic Engine - C++ Custom Engine +# Khaotic Engine Reborn - C++ Custom Engine -Khaotic Engine est un moteur de rendu fait en **C++** réalisé par une petite équipe dans le but d'apprendre à utiliser les API de rendu (OpenGL,DirectX 11/12 et Vulkan). +Khaotic Engine Reborn reprend comme base le moteur Khaotic Engine pour lui ajouter plus de fonctionalité. -Ce moteur est basé sur **DirectX11** utilise **ImGui** et **Boost** avec une couche d'abstraction pour permetre son usage avec d'autres API. +Ce moteur est basé sur **DirectX11** utilise **ImGui** avec une couche d'abstraction pour permetre son usage avec d'autres API. --- @@ -31,77 +31,23 @@ This **DirectX11** based engine uses **ImGui** with an abstraction layer to enab -## Shaders list: +## Nouvauté Aporté par Khaotic Engine Reborn +**SHADER** : - - **Diffuse Lighting** - - **Ambiant Lighting** - - **Specular Lighting** - - **Light Shader** - - **Light Map Shader** - - **Alpha Mapping** - - **Normal Mapping** - - **Specular Mapping** - - **Clipping Planes** - - **Texture Translation** - - **Transparency** - - **Water** - - **Refraction** - - *Reflection (cassé / broken)* - - -*Plus de shaders seront disponibles dans le futur* - ---- - -*More shaders will be added in the future* - - - -## ImGui: - -*Cette partie du moteur est encore en développement, d'autres fonctionnalitées seront ajoutées dans le futur* - -La librairie ImGui est utilisée afin d'intéragir avec les éléments du moteur comme les objets. - -### Fonctionnalitées: -- Importer des objets 3D sous format **.obj** -- Ajout d'un cube à la scène -- Modifier les propriétées d'un objet (Position, Rotation, Taille) -- Modifier les objets présents dans la scène -- Création d'un terrain -- Modifier les propriétées des lumières (Position, Couleur RVB) - ----- -*This part of the engine is still in development, other features will be added in the future* - -ImGui is used to allow interaction between the user and the objects in the scene. - -### Features: -- Import 3D objects with the **.obj** format -- Can add a cube to the scene -- Edit an object properties (Position, Rotation, Scale) -- Edit objects in the current scene -- Terrain generation -- Edit lights properties (Position, RGB Color) +- Cel Shading +**Interface Graphique** : +- Shader Manager ## Demo : [![Demo Video](https://img.youtube.com/vi/qCOCTyB_97c/0.jpg)](https://www.youtube.com/watch?v=qCOCTyB_97c) - -## Bug Report : - -Vous pouvez signalez les problèmes en ouvrant un ticket dans [Issues](https://github.com/GamingCampus-AdrienBourgois/khaotic-engine/issues) - ----- -You can report bugs with the program by creating a ticket in [Issues](https://github.com/GamingCampus-AdrienBourgois/khaotic-engine/issues) - ## Engine Build by : -[](https://github.com/GamingCampus-AdrienBourgois/khaotic-engine?tab=readme-ov-file#engine-build-by-) +**Moteur D'origine** : - [@CatChow0](https://github.com/CatChow0) - [@miragefr0st](https://github.com/miragefr0st) @@ -113,6 +59,6 @@ You can report bugs with the program by creating a ticket in [Issues](https://gi - [@sutabasuto](https://github.com/sutabasuto) +**Moteur Reborn** : - - +- [@CatChow0](https://github.com/CatChow0) diff --git a/enginecustom/CelShadingShader.cpp b/enginecustom/CelShadingShader.cpp new file mode 100644 index 0000000..ddc15e0 --- /dev/null +++ b/enginecustom/CelShadingShader.cpp @@ -0,0 +1,410 @@ +#include "CelShadingShader.h" +#include + +CelShadingShader::CelShadingShader() +{ + m_vertexShader = 0; + m_pixelShader = 0; + m_layout = 0; + m_matrixBuffer = 0; + m_sampleState = 0; + m_lightBuffer = 0; +} + +CelShadingShader::CelShadingShader(const CelShadingShader& other) +{ +} + +CelShadingShader::~CelShadingShader() +{ +} + +bool CelShadingShader::Initialize(ID3D11Device* device, HWND hwnd) +{ + Logger::Get().Log("Initializing CelShadingShader", __FILE__, __LINE__, Logger::LogLevel::Initialize); + + bool result; + wchar_t vsFilename[128]; + wchar_t psFilename[128]; + int error; + + // Set the filename of the vertex shader. + error = wcscpy_s(vsFilename, 128, L"celshading.vs"); + if (error != 0) + { + Logger::Get().Log("Failed to set the filename of the vertex shader", __FILE__, __LINE__); + return false; + } + + // Set the filename of the pixel shader. + error = wcscpy_s(psFilename, 128, L"celshading.ps"); + if (error != 0) + { + Logger::Get().Log("Failed to set the filename of the pixel shader", __FILE__, __LINE__); + return false; + } + + // Initialize the vertex and pixel shaders. + result = InitializeShader(device, hwnd, vsFilename, psFilename); + if (!result) + { + Logger::Get().Log("Failed to initialize the vertex and pixel shaders", __FILE__, __LINE__); + return false; + } + + Logger::Get().Log("Successfully initialized CelShadingShader", __FILE__, __LINE__, Logger::LogLevel::Initialize); + + return true; +} + +void CelShadingShader::Shutdown() +{ + // Shutdown the vertex and pixel shaders as well as the related objects. + ShutdownShader(); +} + +bool CelShadingShader::Render(ID3D11DeviceContext* deviceContext, int indexCount, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix, + ID3D11ShaderResourceView* texture, XMFLOAT3 lightDirection, XMFLOAT4 diffuseColor, XMFLOAT3 lightPosition) +{ + bool result; + // Set the shader parameters that it will use for rendering. + result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture, lightDirection, diffuseColor, lightPosition); + if (!result) + { + Logger::Get().Log("CelShading Error", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } + + // Now render the prepared buffers with the shader. + RenderShader(deviceContext, indexCount); + + return true; +} + +bool CelShadingShader::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename) +{ + HRESULT result; + ID3D10Blob* errorMessage = nullptr; + ID3D10Blob* vertexShaderBuffer = nullptr; + ID3D10Blob* pixelShaderBuffer = nullptr; + D3D11_INPUT_ELEMENT_DESC polygonLayout[3]; + unsigned int numElements; + D3D11_BUFFER_DESC matrixBufferDesc; + D3D11_SAMPLER_DESC samplerDesc; + D3D11_BUFFER_DESC lightBufferDesc; + + // Compile the vertex shader code. + result = D3DCompileFromFile(vsFilename, nullptr, nullptr, "CelShadingVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &vertexShaderBuffer, &errorMessage); + if (FAILED(result)) + { + // If the shader failed to compile it should have written something to the error message. + if (errorMessage) + { + OutputShaderErrorMessage(errorMessage, hwnd, vsFilename); + } + // If there was nothing in the error message then it simply could not find the shader file itself. + else + { + MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK); + } + + return false; + } + + // Compile the pixel shader code. + result = D3DCompileFromFile(psFilename, nullptr, nullptr, "CelShadingPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &pixelShaderBuffer, &errorMessage); + if (FAILED(result)) + { + // If the shader failed to compile it should have written something to the error message. + if (errorMessage) + { + OutputShaderErrorMessage(errorMessage, hwnd, psFilename); + } + // If there was nothing in the error message then it simply could not find the file itself. + else + { + MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK); + } + + return false; + } + + // Create the vertex shader from the buffer. + result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), nullptr, &m_vertexShader); + if (FAILED(result)) + { + return false; + } + + // Create the pixel shader from the buffer. + result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), nullptr, &m_pixelShader); + if (FAILED(result)) + { + return false; + } + + // Create the vertex input layout description. + // This setup needs to match the VertexType structure in the ModelClass and in the shader. + polygonLayout[0].SemanticName = "POSITION"; + polygonLayout[0].SemanticIndex = 0; + polygonLayout[0].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + polygonLayout[0].InputSlot = 0; + polygonLayout[0].AlignedByteOffset = 0; + polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + polygonLayout[0].InstanceDataStepRate = 0; + + polygonLayout[1].SemanticName = "NORMAL"; + polygonLayout[1].SemanticIndex = 0; + polygonLayout[1].Format = DXGI_FORMAT_R32G32B32_FLOAT; + polygonLayout[1].InputSlot = 0; + polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; + polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + polygonLayout[1].InstanceDataStepRate = 0; + + polygonLayout[2].SemanticName = "TEXCOORD"; + polygonLayout[2].SemanticIndex = 0; + polygonLayout[2].Format = DXGI_FORMAT_R32G32_FLOAT; + polygonLayout[2].InputSlot = 0; + polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; + polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + polygonLayout[2].InstanceDataStepRate = 0; + + // Get a count of the elements in the layout. + numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]); + + // Create the vertex input layout. + result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &m_layout); + if (FAILED(result)) + { + return false; + } + + // Release the vertex shader buffer and pixel shader buffer since they are no longer needed. + vertexShaderBuffer->Release(); + vertexShaderBuffer = nullptr; + + pixelShaderBuffer->Release(); + pixelShaderBuffer = nullptr; + + // Setup the description of the dynamic matrix constant buffer that is in the vertex shader. + matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; + matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType); + matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + matrixBufferDesc.MiscFlags = 0; + matrixBufferDesc.StructureByteStride = 0; + + // Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. + result = device->CreateBuffer(&matrixBufferDesc, nullptr, &m_matrixBuffer); + if (FAILED(result)) + { + return false; + } + + // Create a texture sampler state description. + samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.MipLODBias = 0.0f; + samplerDesc.MaxAnisotropy = 1; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; + samplerDesc.BorderColor[0] = 0; + samplerDesc.BorderColor[1] = 0; + samplerDesc.BorderColor[2] = 0; + samplerDesc.BorderColor[3] = 0; + samplerDesc.MinLOD = 0; + samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; + + // Create the texture sampler state. + result = device->CreateSamplerState(&samplerDesc, &m_sampleState); + if (FAILED(result)) + { + return false; + } + + // Setup the description of the light dynamic constant buffer that is in the pixel shader. + lightBufferDesc.Usage = D3D11_USAGE_DYNAMIC; + lightBufferDesc.ByteWidth = sizeof(LightBufferType); + lightBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + lightBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + lightBufferDesc.MiscFlags = 0; + lightBufferDesc.StructureByteStride = 0; + + // Create the constant buffer pointer so we can access the pixel shader constant buffer from within this class. + result = device->CreateBuffer(&lightBufferDesc, nullptr, &m_lightBuffer); + if (FAILED(result)) + { + return false; + } + + return true; +} + + +void CelShadingShader::ShutdownShader() +{ + // Release the light constant buffer. + if (m_lightBuffer) + { + m_lightBuffer->Release(); + m_lightBuffer = nullptr; + } + + // Release the sampler state. + if (m_sampleState) + { + m_sampleState->Release(); + m_sampleState = nullptr; + } + + // Release the matrix constant buffer. + if (m_matrixBuffer) + { + m_matrixBuffer->Release(); + m_matrixBuffer = nullptr; + } + + // Release the layout. + if (m_layout) + { + m_layout->Release(); + m_layout = nullptr; + } + + // Release the pixel shader. + if (m_pixelShader) + { + m_pixelShader->Release(); + m_pixelShader = nullptr; + } + + // Release the vertex shader. + if (m_vertexShader) + { + m_vertexShader->Release(); + m_vertexShader = nullptr; + } +} + +void CelShadingShader::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename) +{ + char* compileErrors; + unsigned long bufferSize, i; + std::ofstream fout; + + // Get a pointer to the error message text buffer. + compileErrors = (char*)(errorMessage->GetBufferPointer()); + + // Get the length of the message. + bufferSize = errorMessage->GetBufferSize(); + + // Open a file to write the error message to. + fout.open("shader-error.txt"); + + // Write out the error message. + for (i = 0; i < bufferSize; i++) + { + fout << compileErrors[i]; + } + + // Close the file. + fout.close(); + + // Release the error message. + errorMessage->Release(); + errorMessage = nullptr; + + // Pop a message up on the screen to notify the user to check the text file for compile errors. + MessageBox(hwnd, L"Error compiling shader. Check shader-error.txt for message.", shaderFilename, MB_OK); +} + + +bool CelShadingShader::SetShaderParameters(ID3D11DeviceContext* deviceContext, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix, + ID3D11ShaderResourceView* texture, XMFLOAT3 lightDirection, XMFLOAT4 diffuseColor, XMFLOAT3 lightPosition) +{ + HRESULT result; + D3D11_MAPPED_SUBRESOURCE mappedResource; + MatrixBufferType* dataPtr; + LightBufferType* dataPtr2; + unsigned int bufferNumber; + + // Transpose the matrices to prepare them for the shader. + worldMatrix = XMMatrixTranspose(worldMatrix); + viewMatrix = XMMatrixTranspose(viewMatrix); + projectionMatrix = XMMatrixTranspose(projectionMatrix); + + // Lock the constant buffer so it can be written to. + result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + if (FAILED(result)) + { + return false; + } + + // Get a pointer to the data in the constant buffer. + dataPtr = (MatrixBufferType*)mappedResource.pData; + + // Copy the matrices into the constant buffer. + dataPtr->world = worldMatrix; + dataPtr->view = viewMatrix; + dataPtr->projection = projectionMatrix; + + // Unlock the constant buffer. + deviceContext->Unmap(m_matrixBuffer, 0); + + // Set the position of the constant buffer in the vertex shader. + bufferNumber = 0; + + // Finally set the constant buffer in the vertex shader with the updated values. + deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer); + + // Lock the light constant buffer so it can be written to. + result = deviceContext->Map(m_lightBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + if (FAILED(result)) + { + return false; + } + + // Get a pointer to the data in the constant buffer. + dataPtr2 = (LightBufferType*)mappedResource.pData; + + // Copy the lighting variables into the constant buffer. + dataPtr2->diffuseColor = diffuseColor; + dataPtr2->lightDirection = lightDirection; + dataPtr2->lightPosition = lightPosition; + dataPtr2->constantAttenuation = 0.5f; // Set your attenuation values here + dataPtr2->linearAttenuation = 0.1f; + dataPtr2->quadraticAttenuation = 0.01f; + + // Unlock the constant buffer. + deviceContext->Unmap(m_lightBuffer, 0); + + // Set the position of the light constant buffer in the pixel shader. + bufferNumber = 0; + + // Finally set the light constant buffer in the pixel shader with the updated values. + deviceContext->PSSetConstantBuffers(bufferNumber, 1, &m_lightBuffer); + + // Set shader texture resource in the pixel shader. + deviceContext->PSSetShaderResources(0, 1, &texture); + + return true; +} + + + +void CelShadingShader::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount) +{ + // Set the vertex input layout. + deviceContext->IASetInputLayout(m_layout); + + // Set the vertex and pixel shaders that will be used to render this triangle. + deviceContext->VSSetShader(m_vertexShader, nullptr, 0); + deviceContext->PSSetShader(m_pixelShader, nullptr, 0); + + // Set the sampler state in the pixel shader. + deviceContext->PSSetSamplers(0, 1, &m_sampleState); + + // Render the triangle. + deviceContext->DrawIndexed(indexCount, 0, 0); +} diff --git a/enginecustom/CelShadingShader.h b/enginecustom/CelShadingShader.h new file mode 100644 index 0000000..141c6a3 --- /dev/null +++ b/enginecustom/CelShadingShader.h @@ -0,0 +1,68 @@ +#ifndef _CELSHADINGSHADER_H_ +#define _CELSHADINGSHADER_H_ + +////////////// +// INCLUDES // +////////////// +#include "Logger.h" +#include +#include +#include +#include +using namespace DirectX; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +// Class name: CelShadingShader +//////////////////////////////////////////////////////////////////////////////// +class CelShadingShader +{ +private: + struct MatrixBufferType + { + XMMATRIX world; + XMMATRIX view; + XMMATRIX projection; + }; + + struct LightBufferType + { + XMFLOAT4 diffuseColor; + XMFLOAT3 lightDirection; + float padding; // Padding to ensure the structure is a multiple of 16 bytes. + XMFLOAT3 lightPosition; // Add light position + float padding2; // Padding to ensure the structure is a multiple of 16 bytes. + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; + float padding3; // Padding to ensure the structure is a multiple of 16 bytes. + }; + + +public: + CelShadingShader(); + CelShadingShader(const CelShadingShader&); + ~CelShadingShader(); + + bool Initialize(ID3D11Device*, HWND); + void Shutdown(); + bool Render(ID3D11DeviceContext*, int, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*, XMFLOAT3, XMFLOAT4, XMFLOAT3); + +private: + bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); + void ShutdownShader(); + void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*); + + bool SetShaderParameters(ID3D11DeviceContext*, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*, XMFLOAT3, XMFLOAT4, XMFLOAT3); + void RenderShader(ID3D11DeviceContext*, int); + +private: + ID3D11VertexShader* m_vertexShader; + ID3D11PixelShader* m_pixelShader; + ID3D11InputLayout* m_layout; + ID3D11Buffer* m_matrixBuffer; + ID3D11SamplerState* m_sampleState; + ID3D11Buffer* m_lightBuffer; +}; + +#endif \ No newline at end of file diff --git a/enginecustom/Spriteclass.cpp b/enginecustom/Spriteclass.cpp index dcb7c42..40aa1e6 100644 --- a/enginecustom/Spriteclass.cpp +++ b/enginecustom/Spriteclass.cpp @@ -329,7 +329,7 @@ void SpriteClass::RenderBuffers(ID3D11DeviceContext* deviceContext) bool SpriteClass::LoadTextures(ID3D11Device* device, ID3D11DeviceContext* deviceContext, char* filename) { - std::string textureFilename; + std::string textureFilename(256, '0'); std::ifstream fin; int i, j; char input; diff --git a/enginecustom/applicationclass.cpp b/enginecustom/applicationclass.cpp index 3e6553b..9cbe31c 100644 --- a/enginecustom/applicationclass.cpp +++ b/enginecustom/applicationclass.cpp @@ -355,6 +355,7 @@ bool ApplicationClass::Initialize(int screenWidth, int screenHeight, HWND hwnd) Logger::Get().Log("Could not initialize the fps string", __FILE__, __LINE__, Logger::LogLevel::Error); return false; } + } catch (const std::exception& e) { @@ -1006,6 +1007,14 @@ bool ApplicationClass::Render(float rotation, float x, float y, float z, float t ambientColor[i] = m_Lights[i]->GetPosition(); } + //Add the 3 first value of the first light position to the TrueLightPosition XMFLOAT3 + positionX = lightPosition[0].x; + positionY = lightPosition[0].y; + positionZ = lightPosition[0].z; + XMFLOAT3 TrueLightPosition = XMFLOAT3(positionX, positionY, positionZ); + + Logger::Get().Log("PositionX: " + std::to_string(positionX) + ", PositionY: " + std::to_string(positionY) + ", PositionZ: " + std::to_string(positionZ), __FILE__, __LINE__, Logger::LogLevel::Debug); + scaleMatrix = XMMatrixScaling(0.5f, 0.5f, 0.5f); // Build the scaling matrix. rotateMatrix = XMMatrixRotationY(rotation); // Build the rotation matrix. translateMatrix = XMMatrixTranslation(x, y, z); // Build the translation matrix. @@ -1040,21 +1049,28 @@ bool ApplicationClass::Render(float rotation, float x, float y, float z, float t cube->Render(m_Direct3D->GetDeviceContext()); - // render the texture using the texture shader. - result = m_ShaderManager->RenderTextureShader(m_Direct3D->GetDeviceContext(), cube->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, cube->GetTexture(0)); - if (!result) - { - Logger::Get().Log("Could not render the cube model using the texture shader", __FILE__, __LINE__, Logger::LogLevel::Error); - return false; + + if (!m_enableCelShading) { + result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), cube->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, cube->GetTexture(0), + diffuseColor, lightPosition, ambientColor); + if (!result) + { + Logger::Get().Log("Could not render the cube model using the light shader", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } } - result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, cube->GetTexture(0), - diffuseColor, lightPosition,ambientColor); - if (!result) - { - Logger::Get().Log("Could not render the cube model using the light shader", __FILE__, __LINE__, Logger::LogLevel::Error); - return false; + // Render cel shading globally to the scene using the cel shader if the checkbox is checked. + if (m_enableCelShading) { + result = m_ShaderManager->RenderCelShadingShader(m_Direct3D->GetDeviceContext(), cube->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, cube->GetTexture(0), + m_Lights[0]->GetDirection(), m_Lights[0]->GetDiffuseColor(), TrueLightPosition); + if (!result) + { + Logger::Get().Log("Could not render the model using the cel shader", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } } + } for (auto& object : m_object) @@ -1072,21 +1088,26 @@ bool ApplicationClass::Render(float rotation, float x, float y, float z, float t object->Render(m_Direct3D->GetDeviceContext()); - // render the texture using the texture shader. - result = m_ShaderManager->RenderTextureShader(m_Direct3D->GetDeviceContext(), object->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, object->GetTexture(0)); - if (!result) - { - Logger::Get().Log("Could not render the cube model using the texture shader", __FILE__, __LINE__, Logger::LogLevel::Error); - return false; + if (!m_enableCelShading) { + result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), object->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, object->GetTexture(0), + diffuseColor, lightPosition, ambientColor); + + if (!result) + { + Logger::Get().Log("Could not render the object model using the light shader", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } } - result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture(0), - diffuseColor, lightPosition, ambientColor); - - if (!result) - { - Logger::Get().Log("Could not render the object model using the light shader", __FILE__, __LINE__, Logger::LogLevel::Error); - return false; + // Render cel shading globally to the scene using the cel shader if the checkbox is checked. + if (m_enableCelShading) { + result = m_ShaderManager->RenderCelShadingShader(m_Direct3D->GetDeviceContext(), object->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, object->GetTexture(0), + m_Lights[0]->GetDirection(), m_Lights[0]->GetDiffuseColor(), TrueLightPosition); + if (!result) + { + Logger::Get().Log("Could not render the model using the cel shader", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } } } @@ -1102,14 +1123,27 @@ bool ApplicationClass::Render(float rotation, float x, float y, float z, float t worldMatrix = XMMatrixMultiply(srMatrix, translateMatrix); chunk->Render(m_Direct3D->GetDeviceContext()); + if (!m_enableCelShading) { + result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), chunk->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, chunk->GetTexture(5), + diffuseColor, lightPosition, ambientColor); - result = m_ShaderManager->RenderlightShader(m_Direct3D->GetDeviceContext(), chunk->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, chunk->GetTexture(5), - diffuseColor, lightPosition, ambientColor); - - if (!result) - { - Logger::Get().Log("Could not render the terrain model using the light shader", __FILE__, __LINE__, Logger::LogLevel::Error); - return false; + if (!result) + { + Logger::Get().Log("Could not render the terrain model using the light shader", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } + } + + + // Render cel shading globally to the scene using the cel shader if the checkbox is checked. + if (m_enableCelShading) { + result = m_ShaderManager->RenderCelShadingShader(m_Direct3D->GetDeviceContext(), chunk->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, chunk->GetTexture(5), + m_Lights[0]->GetDirection(), m_Lights[0]->GetDiffuseColor(), TrueLightPosition); + if (!result) + { + Logger::Get().Log("Could not render the model using the cel shader", __FILE__, __LINE__, Logger::LogLevel::Error); + return false; + } } } diff --git a/enginecustom/applicationclass.h b/enginecustom/applicationclass.h index 377e8a5..fccdea5 100644 --- a/enginecustom/applicationclass.h +++ b/enginecustom/applicationclass.h @@ -83,6 +83,8 @@ public: bool GetShouldQuit() const { return m_ShouldQuit; }; void SetShouldQuit(bool shouldQuit) { m_ShouldQuit = shouldQuit; }; + void SetCelShading(bool enable) { m_enableCelShading = enable; }; + private: bool Render(float, float, float, float, float); bool UpdateMouseStrings(int, int, bool); @@ -134,6 +136,9 @@ private : std::vector m_Lights; int m_numLights; + XMFLOAT3 TrueLightPosition; + ModelClass* m_LightModel; + // ----------------------------------- // // ------------- SHADERS ------------- // // ----------------------------------- // @@ -142,6 +147,8 @@ private : FontShaderClass* m_FontShader; BitmapClass* m_Bitmap; SpriteClass* m_Sprite; + + bool m_enableCelShading; // ----------------------------------- // // ------------ VARIABLES ------------ // diff --git a/enginecustom/celshading.ps b/enginecustom/celshading.ps new file mode 100644 index 0000000..32fd479 --- /dev/null +++ b/enginecustom/celshading.ps @@ -0,0 +1,87 @@ +cbuffer LightBuffer +{ + float4 diffuseColor; + float3 lightDirection; + float padding; // Padding to ensure the structure is a multiple of 16 bytes. + float3 lightPosition; // Add light position + float padding2; // Padding to ensure the structure is a multiple of 16 bytes. + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; + float padding3; // Padding to ensure the structure is a multiple of 16 bytes. +}; + +Texture2D shaderTexture; +SamplerState SampleType; + +struct PixelInputType +{ + float4 position : SV_POSITION; + float3 normal : NORMAL; + float2 tex : TEXCOORD0; + float3 worldPos : TEXCOORD1; // Add world position +}; + +float4 CelShadingPixelShader(PixelInputType input) : SV_TARGET +{ + float4 textureColor; + float lightIntensity; + float4 finalColor; + + // Sample the pixel color from the texture. + textureColor = shaderTexture.Sample(SampleType, input.tex); + + float3 normal = normalize(input.normal); + + // Calculate the light vector from the light position to the world position + float3 lightVector = normalize(lightPosition - input.worldPos); + + // Calculate the light intensity based on the light direction. + float directionalLightIntensity = saturate(dot(normal, normalize(lightDirection))); + + // Calculate the light intensity based on the light position. + float positionalLightIntensity = saturate(dot(normal, lightVector)); + + // Combine the directional and positional light intensities. + lightIntensity = max(directionalLightIntensity, positionalLightIntensity); + + // Calculate the distance from the light to the fragment. + float distance = length(lightPosition - input.worldPos); + + // Apply an attenuation factor based on the distance. + float attenuation = 1.0f / (constantAttenuation + linearAttenuation * distance + quadraticAttenuation * distance * distance); + + // Combine the light intensity with the attenuation factor. + lightIntensity *= attenuation; + + // Apply a step function to create the cel shading effect. + if (lightIntensity > 0.75f) + { + lightIntensity = 1.0f; // Brightest level + } + else if (lightIntensity > 0.5f) + { + lightIntensity = 0.7f; // Mid-bright level + } + else if (lightIntensity > 0.25f) + { + lightIntensity = 0.4f; // Mid-dark level + } + else + { + lightIntensity = 0.1f; // Darkest level + } + + // Simple shadow calculation: if the fragment is behind the light source, it is in shadow. + float3 toLight = normalize(lightPosition - input.worldPos); + float shadow = saturate(dot(normal, toLight)); + if (shadow < 0.1f) + { + lightIntensity *= 0.5f; // Darken the fragment if it is in shadow + } + + // Calculate the final color by combining the texture color with the light intensity and diffuse color. + finalColor = textureColor * diffuseColor * lightIntensity; + + return finalColor; +} diff --git a/enginecustom/celshading.vs b/enginecustom/celshading.vs new file mode 100644 index 0000000..842d724 --- /dev/null +++ b/enginecustom/celshading.vs @@ -0,0 +1,45 @@ +cbuffer MatrixBuffer +{ + matrix worldMatrix; + matrix viewMatrix; + matrix projectionMatrix; +}; + +struct VertexInputType +{ + float4 position : POSITION; + float3 normal : NORMAL; + float2 tex : TEXCOORD0; +}; + +struct PixelInputType +{ + float4 position : SV_POSITION; + float3 normal : NORMAL; + float2 tex : TEXCOORD0; + float3 worldPos : TEXCOORD1; // Add world position +}; + +PixelInputType CelShadingVertexShader(VertexInputType input) +{ + PixelInputType output; + + // Change the position vector to be 4 units for proper matrix calculations. + input.position.w = 1.0f; + + // Calculate the position of the vertex against the world, view, and projection matrices. + float4 worldPosition = mul(input.position, worldMatrix); + output.position = mul(worldPosition, viewMatrix); + output.position = mul(output.position, projectionMatrix); + + // Pass the normal to the pixel shader + output.normal = mul((float3x3)worldMatrix, input.normal); + + // Pass the world position to the pixel shader + output.worldPos = worldPosition.xyz; + + // Store the texture coordinates for the pixel shader. + output.tex = input.tex; + + return output; +} \ No newline at end of file diff --git a/enginecustom/enginecustom.vcxproj b/enginecustom/enginecustom.vcxproj index 8e7cd31..95b3754 100644 --- a/enginecustom/enginecustom.vcxproj +++ b/enginecustom/enginecustom.vcxproj @@ -23,6 +23,7 @@ + @@ -70,6 +71,7 @@ + @@ -154,6 +156,12 @@ Document + + Document + + + Document + Document diff --git a/enginecustom/enginecustom.vcxproj.filters b/enginecustom/enginecustom.vcxproj.filters index c4b2c4c..b273a41 100644 --- a/enginecustom/enginecustom.vcxproj.filters +++ b/enginecustom/enginecustom.vcxproj.filters @@ -171,6 +171,9 @@ Fichiers sources + + Fichiers sources + @@ -317,6 +320,9 @@ Fichiers d%27en-tête + + Fichiers d%27en-tête + @@ -508,5 +514,11 @@ Assets + + shader + + + shader + \ No newline at end of file diff --git a/enginecustom/imgui.ini b/enginecustom/imgui.ini index fc8a95f..45d976f 100644 --- a/enginecustom/imgui.ini +++ b/enginecustom/imgui.ini @@ -3,18 +3,22 @@ Pos=60,60 Size=400,400 [Window][Khaotic Engine] -Pos=429,57 -Size=392,218 +Pos=509,25 +Size=392,273 [Window][Objects] -Pos=39,222 -Size=589,294 +Pos=934,36 +Size=457,294 [Window][Terrain] Pos=60,60 Size=342,82 [Window][Light] -Pos=95,296 -Size=345,230 +Pos=1564,17 +Size=345,519 + +[Window][Shader Manager] +Pos=32,253 +Size=172,284 diff --git a/enginecustom/imguiManager.cpp b/enginecustom/imguiManager.cpp index 5cc5ea0..002e579 100644 --- a/enginecustom/imguiManager.cpp +++ b/enginecustom/imguiManager.cpp @@ -111,6 +111,17 @@ void imguiManager::WidgetAddObject(ApplicationClass* app) } } +void imguiManager::WidgetShaderWindow(ApplicationClass* app) +{ + ImGui::Begin("Shader Manager"); + + // Checkbox for toggling cel shading globally in the application class by calling the SetCelShading function in the application class when the checkbox state changes + ImGui::Checkbox("Enable Cel Shading", &m_EnableCelShading); + app->SetCelShading(m_EnableCelShading); + + ImGui::End(); +} + void imguiManager::WidgetObjectWindow(ApplicationClass* app) { ImGui::Begin("Objects", &showObjectWindow); @@ -300,6 +311,11 @@ bool imguiManager::ImGuiWidgetRenderer(ApplicationClass* app) showLightWindow = true; } + if (ImGui::Button("Open Shader Window")) + { + showShaderWindow = true; + } + ImGui::End(); // Show windows if their corresponding variables are true @@ -318,6 +334,11 @@ bool imguiManager::ImGuiWidgetRenderer(ApplicationClass* app) WidgetLightWindow(app); } + if (showShaderWindow) + { + WidgetShaderWindow(app); + } + //render imgui Render(); @@ -352,7 +373,6 @@ void imguiManager::WidgetLightWindow(ApplicationClass* app) app->SetLightColor(index, XMVectorSet(col[0], col[1], col[2], 0.0f)); } - ImGui::Separator(); } index++; }; diff --git a/enginecustom/imguiManager.h b/enginecustom/imguiManager.h index c814210..a6977cb 100644 --- a/enginecustom/imguiManager.h +++ b/enginecustom/imguiManager.h @@ -31,13 +31,19 @@ public: void WidgetObjectWindow(ApplicationClass* app); void WidgetTerrainWindow(ApplicationClass* app); void WidgetLightWindow(ApplicationClass* app); + void WidgetShaderWindow(ApplicationClass* app); bool ImGuiWidgetRenderer(ApplicationClass* app); + // Shader toggles + + bool m_EnableCelShading; + private : bool showObjectWindow = false; bool showTerrainWindow = false; bool showLightWindow = false; + bool showShaderWindow = false; private: ImGuiIO* io; diff --git a/enginecustom/shadermanagerclass.cpp b/enginecustom/shadermanagerclass.cpp index a6d5be3..4ad7658 100644 --- a/enginecustom/shadermanagerclass.cpp +++ b/enginecustom/shadermanagerclass.cpp @@ -13,6 +13,7 @@ ShaderManagerClass::ShaderManagerClass() m_LightMapShader = 0; m_RefractionShader = 0; m_WaterShader = 0; + m_CelShadingShader = 0; } @@ -140,6 +141,14 @@ bool ShaderManagerClass::Initialize(ID3D11Device* device, HWND hwnd) return false; } + m_CelShadingShader = new CelShadingShader; + + result = m_CelShadingShader->Initialize(device, hwnd); + if (!result) + { + return false; + } + Logger::Get().Log("ShaderManagerClass initialized", __FILE__, __LINE__, Logger::LogLevel::Initialize); return true; @@ -417,4 +426,18 @@ bool ShaderManagerClass::RenderWaterShader(ID3D11DeviceContext* deviceContext, i } return true; +} + +bool ShaderManagerClass::RenderCelShadingShader(ID3D11DeviceContext* deviceContext, int indexCount, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix, + ID3D11ShaderResourceView* texture, XMFLOAT3 lightDirection, XMFLOAT4 diffuseColor, XMFLOAT3 lightPosition) +{ + bool result; + + result = m_CelShadingShader->Render(deviceContext, indexCount, worldMatrix, viewMatrix, projectionMatrix, texture, lightDirection, diffuseColor, lightPosition); + if (!result) + { + return false; + } + + return true; } \ No newline at end of file diff --git a/enginecustom/shadermanagerclass.h b/enginecustom/shadermanagerclass.h index 20de267..c0aa0ba 100644 --- a/enginecustom/shadermanagerclass.h +++ b/enginecustom/shadermanagerclass.h @@ -15,6 +15,7 @@ #include "lightmapshaderclass.h" #include "refractionshaderclass.h" #include "watershaderclass.h" +#include "CelShadingShader.h" //////////////////////////////////////////////////////////////////////////////// @@ -43,6 +44,7 @@ public: XMFLOAT3, XMFLOAT4[], XMFLOAT4[], XMFLOAT4[], XMFLOAT4); bool RenderWaterShader(ID3D11DeviceContext*, int, XMMATRIX, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*, ID3D11ShaderResourceView*, ID3D11ShaderResourceView*, float, float); + bool RenderCelShadingShader(ID3D11DeviceContext*, int, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*, XMFLOAT3, XMFLOAT4, XMFLOAT3); private: TextureShaderClass* m_TextureShader; NormalMapShaderClass* m_NormalMapShader; @@ -56,6 +58,7 @@ private: LightMapShaderClass* m_LightMapShader; RefractionShaderClass* m_RefractionShader; WaterShaderClass* m_WaterShader; + CelShadingShader* m_CelShadingShader; }; #endif \ No newline at end of file