0
\$\begingroup\$

I am working on my first Direct3D game and even after many days of searching Internet and experimenting I can't figure out how to work with normals properly. As a result, the lights are not working correctly. The scene is shown on the below picture.

Scene sample

The camera is always pointed into center of the scene (0,0,0) and rotates around it. The scene itself is static, only the vehicles can move forward or backward. The light source is a static point light. So I would presume the diffuse lighting level of the objects must be static during camera rotation. But it isn't. When I rotate the camera, the lighting levels of particular parts of objects are changing (even if I disable the specular part of light). Also the diffuse light on the objects is not correct with respect to the light position. So I would say I am doing something completely wrong with the normals. I am trying to figure it out for 1 week but with no success. So I am kindly asking you for any help which can push me forward.

I have found (e.g. here), that I must multiply my normal vectors with transposed inverse matrix of the original world matrix applied to my vertices, because my models are scaled. But this is not helping. Below are relevant parts of my code.

Render scene method:

struct CBUFFER {
    DirectX::XMMATRIX world;
    DirectX::XMMATRIX rotation;
    DirectX::XMMATRIX invTrWorld;
    DirectX::XMVECTOR cameraPosition;
    DirectX::XMVECTOR lightVector;
    DirectX::XMVECTOR diffuseColor;
    DirectX::XMVECTOR ambientColor;
    float specularPower;
    DirectX::XMVECTOR specularColor;
};

void Game::Render() {
    CBUFFER cBuffer;
    XMMATRIX matView, matPerspective;
    XMVECTOR camPosition = XMVectorSet(0.0f, 4.0f, -10.0f, 0.0f);
    camPosition = XMVector4Transform(camPosition, _rotation); // _rotation - defines the angle of the camera

    //Set the View matrix
    matView = XMMatrixLookAtLH(
        camPosition,                          // the camera position (rotating around the center of the board)
        XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f),  // the look-at position
        XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)   // the up direction
    );
    cBuffer.cameraPosition = camPosition;

    // create a projection matrix
    matPerspective = XMMatrixPerspectiveFovLH((FLOAT)XMConvertToRadians(45), (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, 1.0f, 100.0f);

    cBuffer.lightVector = XMVectorSet(-10.0f, 10.0f, 10.0f, 1.0f);
    cBuffer.diffuseColor = XMVectorSet(1.0f, 1.0f, 1.0f, 1.0f);
    cBuffer.ambientColor = XMVectorSet(_ambientColorIntensity, _ambientColorIntensity, _ambientColorIntensity, 1.0f);
    cBuffer.specularColor = XMVectorSet(1.0f, 1.0f, 1.0f, 1.0f);

    // clear the back buffer to a deep blue
    FLOAT bgColor[4] = { 0.0f, 0.2f, 0.4f, 1.0f };
    _d3d->GetDeviceContext()->ClearRenderTargetView(_d3d->GetBackBuffer(), bgColor);

    // clear the depth buffer
    _d3d->GetDeviceContext()->ClearDepthStencilView(_d3d->GetZBuffer(), D3D11_CLEAR_DEPTH, 1.0f, 0);

    // select which vertex buffer to display
    UINT stride = sizeof(VERTEX);
    UINT offset = 0;
    _d3d->GetDeviceContext()->IASetVertexBuffers(0, 1, _d3d->GetVBufferAddr(), &stride, &offset);
    _d3d->GetDeviceContext()->IASetIndexBuffer(_d3d->GetIBuffer(), DXGI_FORMAT_R32_UINT, 0);

    // select which primtive type we are using
    _d3d->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    _d3d->GetDeviceContext()->IASetInputLayout(_d3d->GetLayout());

    // select Rasterizer and Sampler configuration
    _d3d->GetDeviceContext()->RSSetState(_d3d->GetRState());
    _d3d->GetDeviceContext()->PSSetSamplers(0, 1, _d3d->GetSStateAddr());

    // Draw model instances - Non-vehicle objects
    for (auto it = _minstances.begin(); it != _minstances.end(); it++) {
        ModelInstance mi = it->second;

        // Store instance transformation into constant buffer
        XMMATRIX worldMatrix = mi.GetTransformation() * _worldOffset;
        worldMatrix *= matView * matPerspective;
        XMMATRIX trInvWorld = XMMatrixInverse(nullptr, XMMatrixTranspose(worldMatrix));
        cBuffer.world = worldMatrix;
        cBuffer.invTrWorld = invTrWorld;
        // TODO: obtain specularPower from model
        cBuffer.specularPower = 1.0f;
        // Send constant buffer
        _d3d->GetDeviceContext()->UpdateSubresource(_d3d->GetCBuffer(), 0, 0, &cBuffer, 0, 0);

        for (auto i : mi.GetModel().GetMeshEntries()) {
            // select texture
            _d3d->GetDeviceContext()->PSSetShaderResources(0, 1, &i._pTexture);
            _d3d->GetDeviceContext()->DrawIndexed(i._numIndices, i._baseIndex, i._baseVertex);
        }
    }

    // Draw vehicles
    for (auto it = _vehicles.begin(); it != _vehicles.end(); it++) {
        Vehicle mi = it->second;
        if (mi.IsHidden()) {
            continue;
        }
        // Store vehicle transformation into constant buffer
        XMMATRIX worldMatrix = mi.GetTransformation() * _worldOffset;
        worldMatrix *= matView * matPerspective;
        XMMATRIX trInvWorld = XMMatrixInverse(nullptr, XMMatrixTranspose(worldMatrix));
        cBuffer.world = worldMatrix;
        cBuffer.invTrWorld = invTrWorld;
        // Store vehicle color into constant buffer
        XMVECTOR vehicleColor = mi.GetColor();
        cBuffer.diffuseColor = vehicleColor;
        cBuffer.specularColor = vehicleColor;
        float glowIntensity = _ambientColorIntensity + mi.GetGlowLevel();
        cBuffer.ambientColor = XMVectorSet(vehicleColor.m128_f32[0] * glowIntensity,
            vehicleColor.m128_f32[1] * glowIntensity,
            vehicleColor.m128_f32[2] * glowIntensity,
            vehicleColor.m128_f32[3] * glowIntensity);
        // TODO: obtain specularPower from model
        cBuffer.specularPower = 32.0f;
        // Send constant buffer
        _d3d->GetDeviceContext()->UpdateSubresource(_d3d->GetCBuffer(), 0, 0, &cBuffer, 0, 0);

        for (auto i : mi.GetModel().GetMeshEntries()) {
            // select texture
            _d3d->GetDeviceContext()->PSSetShaderResources(0, 1, &i._pTexture);
            _d3d->GetDeviceContext()->DrawIndexed(i._numIndices, i._baseIndex, i._baseVertex);
        }
    }

    // print FPS info
    _d2d->PrintInfo();

    // switch the back buffer and the front buffer
    _d3d->GetSwapChain()->Present(0, 0);
}

HLSL code:

cbuffer ConstantBuffer {
    matrix world;
    matrix rotation;
    matrix trinvworld;
    float4 camposition; // position of the camera
    float4 lightpos; // the diffuse light's vector
    float4 diffusecol; // the diffuse light's color
    float4 ambientcol; // the ambient light's color
    float specularpow; // the specular light's power
    float4 specularcol; // the specular light's color
}

Texture2D Texture;
SamplerState ss;

struct VOut {
    float4 color : COLOR;
    float2 texcoord : TEXCOORD0;
    float4 position : SV_POSITION;
};

VOut VShader(float4 position : POSITION, float3 normal : NORMAL, float2 texcoord : TEXCOORD) {
    VOut output;

    // VERTEX POSITION TRANSFORMATION
    // Calculate the position of the vertex in the world
    float4 worldposition4 = mul(world, position);
//  float3 worldposition = mul((float3x3)world, position.xyz);
    output.position = worldposition4

    // AMBIENT LIGHT & NORMAL TRANSFORMATION
    float4 color = ambientcol;
    float3 norm = normalize(mul((float3x3)trinvworld, normal));
    //float3 norm = normalize(mul((float3x3)world, normal));

    // DIFFUSE LIGHT
//  float3 lightpos3 = lightpos.xyz;
//  float3 lightvec = normalize(lightpos3 - worldposition);
    float4 lightvec = normalize(lightpos - worldposition4);
    lightvec = -lightvec;
    float lightintensity = saturate(dot(norm, lightvec)); // calculate the amount of light

    // SPECULAR LIGHT
    //float3 camposition3 = camposition.xyz;
    float3 camvec = normalize(camposition - worldposition4);
    camvec = -camvec;
    float3 reflectedlight = reflect(lightvec, norm);

    float specularfactor = saturate(dot(reflectedlight, camvec));
    float dampedfactor = pow(specularfactor, specularpow);
    float4 finalspecular = dampedfactor * specularcol;

    // COMBINE ALL LIGTHS TOGETHER
    color += diffusecol * lightintensity + finalspecular;
    output.color = saturate(color);

    // TEXTURE
    output.texcoord = texcoord;

    return output;
}

float4 PShader(float4 color : COLOR, float2 texcoord : TEXCOORD) : SV_TARGET
{
    return color *Texture.Sample(ss, texcoord);
}

The normals are part of loaded models. The models are loaded using Assimp Importer:

Model::Model(const char* pFile, ID3D11Device* dev) {
    const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
    _pScene = _imp.ReadFile(pFile, aiProcess_FlipUVs | aiProcess_FixInfacingNormals | aiProcess_MakeLeftHanded |
        aiProcess_GenSmoothNormals | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
    if (!_pScene) {
    }else {
        if (_pScene->mNumMeshes < 1) {
        }
        else {
            // Process all meshes
            for (unsigned int m = 0; m < _pScene->mNumMeshes; m++) {
                _pMesh = _pScene->mMeshes[m];
                MeshEntry me;
                me._baseVertex = Model::_objectVertices.size();
                me._baseIndex = _objectIndices.size();
                if (_pMesh) {
                    // Load vertices
                    Model::_objectVertices.reserve(Model::_objectVertices.size() + _pMesh->mNumVertices);
                    for (unsigned int i = 0; i < _pMesh->mNumVertices; i++) {
                        const aiVector3D* pPos = &(_pMesh->mVertices[i]);
                        const aiVector3D* pNormal = (_pMesh->mNormals != nullptr) ? &(_pMesh->mNormals[i]) : &Zero3D;
                        const aiVector3D* pTexCoord = (_pMesh->HasTextureCoords(0)) ? &(_pMesh->mTextureCoords[0][i]) : &Zero3D;
                        VERTEX v;
                        v.pos.x = pPos->x; v.pos.y = pPos->y; v.pos.z = pPos->z;
                        v.normal.x = pNormal->x; v.normal.y = pNormal->y; v.normal.z = pNormal->z;
                        //v.normal.x = 1.0f; v.normal.y = 0.0f; v.normal.z = 0.0f;
                        v.textCoord.x = pTexCoord->x; v.textCoord.y = pTexCoord->y;
                        Model::_objectVertices.push_back(v);
                    }

                    // Load indices
                    _objectIndices.reserve(_objectIndices.size() + (_pMesh->mNumFaces * 3));
                    for (unsigned int i = 0; i < _pMesh->mNumFaces; i++) {
                        if (_pMesh->mFaces[i].mNumIndices == 3) {
                            _objectIndices.push_back(_pMesh->mFaces[i].mIndices[0]);
                            _objectIndices.push_back(_pMesh->mFaces[i].mIndices[1]);
                            _objectIndices.push_back(_pMesh->mFaces[i].mIndices[2]);
                            me._numIndices += 3;
                        }
                    }

                    // Process material of the mesh
                    if (_pScene->HasMaterials()) {
                        aiMaterial* material = _pScene->mMaterials[_pMesh->mMaterialIndex];
                        aiString aiTextureFile;
                        me._materialIndex = _pMesh->mMaterialIndex;
                        // Load texture file
                        if (material->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
                            material->GetTexture(aiTextureType_DIFFUSE, 0, &aiTextureFile);
                            // Convert aiString to LPWSTR
                            size_t size = strlen(aiTextureFile.C_Str()) + 1; // plus null
                            wchar_t* wcTextureFile = new wchar_t[size];
                            std::shared_ptr<wchar_t> sp(wcTextureFile, std::default_delete<wchar_t[]>());
                            size_t outSize;
                            mbstowcs_s(&outSize, wcTextureFile, size, aiTextureFile.C_Str(), size - 1);
                            LPWSTR textureFile = wcTextureFile;
                            CreateWICTextureFromFile(dev, nullptr, textureFile, nullptr, &(me._pTexture), 0);
                            if (me._pTexture == nullptr) {
                                CreateDDSTextureFromFile(dev, nullptr, textureFile, nullptr, &(me._pTexture), 0);
                            }
                        }
                    }
                    // If no texture found, use the default one
                    if (me._pTexture == nullptr) {
                        CreateWICTextureFromFile(dev, nullptr, L"models/default.jpg", nullptr, &(me._pTexture), 0);
                    }
                    _entries.push_back(me);
                }
            }
        }
    }
}

And the D3D scene is initialized by D3D class.

D3D.h

#pragma once

#include <d3d11.h>
#include <directxmath.h>
#include <vector>
#include "RushHour.h"
#include "D3DSupplementary.h"

class D3D {
public:
    D3D() = delete;
    D3D(HWND hWnd);
    D3D(D3D&&) = default;
    D3D& operator= (D3D&&) = default;
    ~D3D();
    // D3D must not be copied, as it would release all elements after copy
    D3D(const D3D&) = delete;
    D3D& operator=(const D3D&) = delete;

    void CreateVertexBuffer(std::vector<VERTEX> OurVertices);
    void CreateIndexBuffer(std::vector<UINT> OurIndices);

    IDXGISwapChain* GetSwapChain() const { return _swapChain; };
    IDXGIDevice* GetDXGIDevice() const { return _dxgiDevice; }
    ID3D11Device* GetDevice() const { return _dev; }
    ID3D11DeviceContext* GetDeviceContext() const { return _devCon; }
    ID3D11RenderTargetView* GetBackBuffer() const { return _bBuffer; }
    ID3D11DepthStencilView* GetZBuffer() const { return _zBuffer; }
    ID3D11Buffer* GetCBuffer() const { return _cBuffer; }
    ID3D11Buffer* GetVBuffer() const { return _vBuffer; }
    ID3D11Buffer** GetVBufferAddr() { return &(_vBuffer); }
    ID3D11Buffer* GetIBuffer() const { return _iBuffer; }
    ID3D11InputLayout* GetLayout() const { return _layout; }
    ID3D11RasterizerState* GetRState() const { return _rs;  }
    ID3D11SamplerState* GetSState() const { return _ss; }
    ID3D11SamplerState** GetSStateAddr() { return &(_ss); }

private:
    IDXGISwapChain* _swapChain;             // swap chain interface
    IDXGIDevice* _dxgiDevice;
    ID3D11Device* _dev;                     // device interface
    ID3D11DeviceContext* _devCon;           // device context
    ID3D11VertexShader* _vs;                // vertex shader
    ID3D11PixelShader* _ps;                 // pixel shader
    ID3D11InputLayout* _layout;             // layout
    ID3D11RenderTargetView* _bBuffer;       // backbuffer
    ID3D11DepthStencilView* _zBuffer;       // depth buffer
    ID3D11Buffer* _cBuffer;                 // constant buffer
    ID3D11Buffer* _vBuffer;                 // vertex buffer                                      
    ID3D11Buffer* _iBuffer;                 // index buffer

    // State objects
    ID3D11RasterizerState* _rs;            // the default rasterizer state
    ID3D11SamplerState* _ss;               // sampler state

    void CreateDevice(HWND hWnd);
    void CreateDepthBuffer();
    void CreateRenderTarget();
    void SetViewport();
    void LoadShaders();
    void CreateConstantBuffer();
    void InitRasterizer();
    void InitSampler();
};

D3D.cpp:

#include "stdafx.h"
#include <d3dcompiler.h>
#include "CommonException.h"
#include "D3D.h"


D3D::D3D(HWND hWnd) {
    // this function initializes and prepares Direct3D for use
    CreateDevice(hWnd);
    CreateDepthBuffer();
    CreateRenderTarget();
    SetViewport();
    LoadShaders();
    CreateConstantBuffer();
    InitRasterizer();
    InitSampler();
}


D3D::~D3D() {
    // switch to windowed mode
    if (_swapChain) _swapChain->SetFullscreenState(FALSE, NULL);

    // release all the stuff
    if (_zBuffer) _zBuffer->Release();
    if (_layout) _layout->Release();
    if (_vs) _vs->Release();
    if (_ps) _ps->Release();
    if (_vBuffer) _vBuffer->Release();
    if (_iBuffer) _iBuffer->Release();
    if (_cBuffer) _cBuffer->Release();
    if (_swapChain) _swapChain->Release();
    if (_bBuffer) _bBuffer->Release();
    if (_rs) _rs->Release();
    if (_ss) _ss->Release();
    if (_dev) _dev->Release();
    if (_devCon) _devCon->Release();
}

void D3D::CreateDevice(HWND hWnd) {
    DXGI_SWAP_CHAIN_DESC scd;
    ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

    // fill the swap chain description struct
    scd.BufferDesc.Width = SCREEN_WIDTH;                    // set the back buffer width
    scd.BufferDesc.Height = SCREEN_HEIGHT;                  // set the back buffer height
    scd.BufferCount = 1;                                    // one back buffer
    scd.BufferDesc.RefreshRate.Numerator = 0;                                   // refresh rate: 0 -> do not care
    scd.BufferDesc.RefreshRate.Denominator = 1;
    scd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;     // use 32-bit color
    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // how swap chain is to be used
    scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;     // unspecified scan line ordering
    scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;                     // unspecified scaling
    scd.OutputWindow = hWnd;                                // the window to be used
    scd.SampleDesc.Count = 4;                               // how many multisamples
    scd.SampleDesc.Quality = 0;
    scd.Windowed = RUNINWINDOW;                             // windowed/full-screen mode
    scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;     // allow full-screen switching by Alt-Enter

    D3D_FEATURE_LEVEL featureLevel;

    // create a device, device context and swap chain using the information in the scd struct
    if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 
        D3D11_CREATE_DEVICE_BGRA_SUPPORT /* This flag is necessary for compatibility with Direct2D */, 
        NULL, NULL, D3D11_SDK_VERSION, &scd, &_swapChain, &_dev, &featureLevel, &_devCon))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create the Direct3D device!");
    } else if (featureLevel < D3D_FEATURE_LEVEL_11_0) {
        throw CommonException((LPWSTR)L"Critical error: You need DirectX 11.0 or higher to run this game!");
    }

    if (FAILED(_dev->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<LPVOID*>(&_dxgiDevice)))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to get Direct3D DXGI device!");
    }
}

void D3D::CreateDepthBuffer() {
    // create the depth buffer texture
    D3D11_TEXTURE2D_DESC texd;
    ZeroMemory(&texd, sizeof(texd));
    texd.Width = SCREEN_WIDTH;
    texd.Height = SCREEN_HEIGHT;
    texd.ArraySize = 1;
    texd.MipLevels = 1;
    texd.SampleDesc.Count = 4;
    texd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    texd.Usage = D3D11_USAGE_DEFAULT;
    texd.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    ID3D11Texture2D *pDepthBuffer;
    if (FAILED(_dev->CreateTexture2D(&texd, NULL, &pDepthBuffer))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D depth buffer texture!");
    }

    // create the depth buffer
    D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
    ZeroMemory(&dsvd, sizeof(dsvd));
    //dsvd.Format = DXGI_FORMAT_D32_FLOAT;
    dsvd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    dsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
    if (FAILED(_dev->CreateDepthStencilView(pDepthBuffer, &dsvd, &_zBuffer))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D depth buffer!");
    }
    pDepthBuffer->Release();
}

void D3D::CreateRenderTarget() {
    // create backbuffer and render target
    ID3D11Texture2D *pBackBuffer;
    if (FAILED(_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<LPVOID*>(&pBackBuffer)))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to get Direct3D depth buffer!");
    }
    if (FAILED(_dev->CreateRenderTargetView(pBackBuffer, NULL, &_bBuffer))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D depth buffer!");
    }
    pBackBuffer->Release();
    _devCon->OMSetRenderTargets(1, &_bBuffer, _zBuffer);
}

void D3D::SetViewport() {
    // set the viewport
    D3D11_VIEWPORT viewport;
    ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));

    viewport.TopLeftX = 0;
    viewport.TopLeftY = 0;
    viewport.Width = SCREEN_WIDTH;
    viewport.Height = SCREEN_HEIGHT;
    viewport.MinDepth = 0;
    viewport.MaxDepth = 1;

    _devCon->RSSetViewports(1, &viewport);
}

void D3D::LoadShaders() {
    // load and compile vertex and pixel shader
    ID3D10Blob *VS, *PS;
    D3DCompileFromFile(L"shaders.shader", NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, "VShader", "vs_4_0", 0, 0, &VS, NULL);
    D3DCompileFromFile(L"shaders.shader", NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, "PShader", "ps_4_0", 0, 0, &PS, NULL);
    if (FAILED(_dev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &_vs))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D vertex shader!");
    }
    if (FAILED(_dev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &_ps))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D pixel shader!");
    }
    _devCon->VSSetShader(_vs, 0, 0);
    _devCon->PSSetShader(_ps, 0, 0);

    // create the input layout object
    D3D11_INPUT_ELEMENT_DESC ied[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };

    if (FAILED(_dev->CreateInputLayout(ied, 3, VS->GetBufferPointer(), VS->GetBufferSize(), &_layout))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D input layout!");
    }
    _devCon->IASetInputLayout(_layout);
}

void D3D::CreateConstantBuffer() {
    // constant buffer
    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd, sizeof(bd));
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(CBUFFER);
    bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    if (FAILED(_dev->CreateBuffer(&bd, NULL, &_cBuffer))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D constant buffer!");
    }
    _devCon->VSSetConstantBuffers(0, 1, &_cBuffer);
}

void D3D::CreateVertexBuffer(std::vector<VERTEX> OurVertices) {
    // create the vertex buffer
    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd, sizeof(bd));
    bd.Usage = D3D11_USAGE_DYNAMIC;
    bd.ByteWidth = sizeof(VERTEX) * OurVertices.size();
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    if (FAILED(_dev->CreateBuffer(&bd, NULL, &_vBuffer))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D vertex buffer!");
    }

    // copy the vertices into the buffer
    D3D11_MAPPED_SUBRESOURCE ms;
    if (FAILED(_devCon->Map(_vBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to map Direct3D vertex buffer!");
    }
    memcpy(ms.pData, OurVertices.data(), sizeof(VERTEX) * OurVertices.size());
    _devCon->Unmap(_vBuffer, NULL);
}

void D3D::CreateIndexBuffer(std::vector<UINT> OurIndices) {
    // create the index buffer
    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd, sizeof(bd));
    bd.Usage = D3D11_USAGE_DYNAMIC;
    bd.ByteWidth = sizeof(UINT) * OurIndices.size();
    bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
    bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    bd.MiscFlags = 0;
    if (FAILED(_dev->CreateBuffer(&bd, NULL, &_iBuffer))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D index buffer!");
    }

    // copy the indices into the buffer
    D3D11_MAPPED_SUBRESOURCE ms;
    if (FAILED(_devCon->Map(_iBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to map Direct3D index buffer!");
    }
    memcpy(ms.pData, OurIndices.data(), sizeof(UINT) * OurIndices.size());
    _devCon->Unmap(_iBuffer, NULL);
}

void D3D::InitRasterizer() {
    D3D11_RASTERIZER_DESC rd;
    rd.FillMode = D3D11_FILL_SOLID;
    rd.CullMode = D3D11_CULL_BACK;
    rd.FrontCounterClockwise = TRUE;
    rd.DepthClipEnable = TRUE;
    rd.ScissorEnable = FALSE;
    rd.AntialiasedLineEnable = FALSE;
    rd.MultisampleEnable = FALSE;
    rd.DepthBias = 0;
    rd.DepthBiasClamp = 0.0f;
    rd.SlopeScaledDepthBias = 0.0f;
    if (FAILED(_dev->CreateRasterizerState(&rd, &_rs))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to creat Direct3D rasterizer state!");
    }
}

void D3D::InitSampler() {
    D3D11_SAMPLER_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    sd.MaxAnisotropy = 16;
    sd.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    sd.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    sd.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    sd.BorderColor[0] = 0.0f;
    sd.BorderColor[1] = 0.0f;
    sd.BorderColor[2] = 0.0f;
    sd.BorderColor[3] = 0.0f;
    sd.ComparisonFunc = D3D11_COMPARISON_NEVER;
    sd.MinLOD = 0.0f;
    sd.MaxLOD = FLT_MAX;
    sd.MipLODBias = 0.0f;
    if (FAILED(_dev->CreateSamplerState(&sd, &_ss))) {
        throw CommonException((LPWSTR)L"Critical error: Unable to creat Direct3D sampler state!");
    }
}

I hope I have provided all important parts of the code. Just for case, the complete code can be found here.

Another thing I don't understand in my app might not be connected with my problem at all, but maybe it is connected so I rather mention it here:

Transformation matrices are ok to be stored into constant buffer in original form (== not transposed). As far as I understood from articles I found (e.g. here) the CPU stores arrays in row-major format while HLSL needs them in column-major format so I must transpose all matrices before I store them into the constant buffer. But in my project it works without transposed matrices. When I transpose them, the scene is completely broken.

\$\endgroup\$
7
  • \$\begingroup\$ Why do you compute your world position twice, once with translation (float4 worldposition4 = mul(world, position);), and once without (float3 worldposition = mul((float3x3)world, position.xyz);)? \$\endgroup\$ – DMGregory Apr 1 '18 at 21:58
  • \$\begingroup\$ I don't see where you are calculating your diffuse light, which is usually the dot of the surface normal with the diffuse light direction. All you are doing is multiplying the diffusecolour by your point light vector. You need to multiply by the diffuselight direction. That's very different to the point light. \$\endgroup\$ – ErnieDingo Apr 1 '18 at 22:07
  • \$\begingroup\$ @ErnieDingo - it is done in the HLSL code float4 lightvec = normalize(lightpos - worldposition4); lightvec = -lightvec; float lightintensity = saturate(dot(norm, lightvec)); // calculate the amount of light color += diffusecol * lightintensity + finalspecular; output.color = saturate(color); \$\endgroup\$ – Jan Pazdera Apr 1 '18 at 22:10
  • \$\begingroup\$ @DMGregory - I was just trying whether it makes any difference - it does not. It is just a remain of my experiments. \$\endgroup\$ – Jan Pazdera Apr 1 '18 at 22:17
  • \$\begingroup\$ your light intensity for a diffuse light should be a static single direction vector that does not get calculated by the position of the vertex. \$\endgroup\$ – ErnieDingo Apr 1 '18 at 22:20
0
\$\begingroup\$

Your "world" matrix isn't really a world matrix. It's a world/model view projection matrix. That means it varies with your viewpoint, causing all your lighting calculations that depend on it to also vary with viewpoint.

You'll want to send (up to) three matrices down the pipe:

  1. Object-to-World matrix, transforming model space positions to world space.

    Use this matrix to get your world positions for comparing against your worldspace light & camera positions for your worldspace lighting calculations.

  2. Model View Projection or MVP matrix, transforming model space positions into world space, into view space, and ultimately into perspective projection space.

    Use this matrix (or optionally a View-Projection matrix multiplied by the worldspace position calculated in step 1) to produce your SV_POSITION for projecting your vertices onto the screen.

  3. Inverse Transpose of 1, if you're using non-uniform scales anywhere.

    Use this to transform your normal vectors. If all your scales are uniform, then you can re-use the World matrix in step 1 for this task too.

\$\endgroup\$
3
  • \$\begingroup\$ Thank you. I have edited the code (see the edit - Render function and HLSL) to provide three matrices - world, mvp and inverted transposed world. The scene changed, but still not ok. I must play with that and then will let you know. \$\endgroup\$ – Jan Pazdera Apr 1 '18 at 22:47
  • \$\begingroup\$ Not much I can do with "not ok" as the only symptom. Remember we can't see what you see, so the more information you can give us, the better we can help. At a glance though, you probably want to strip off the w component and work in 3 dimensions only after you've transformed your world position. The fourth component helps for matrix multiplications but can trip you up elsewhere. Taking the camera direction as a 4-vector and normalizing it is not necessarily meaningful, for instance. \$\endgroup\$ – DMGregory Apr 1 '18 at 22:48
  • \$\begingroup\$ "Not ok" situation was caused by wrong specular light. When I disable it, the scene looks much better now. Tomorrow I will try to play with that again (it is early morning here and I finally must go to bed :) and let you know. Thank you very much for your help. So far it looks it might solve my problem. \$\endgroup\$ – Jan Pazdera Apr 1 '18 at 23:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.